This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 257013
Collapse All | Expand All

(-)a/ide/launcher/netbeans.conf (-1 / +1 lines)
Lines 43-49 Link Here
43
# Concurrent Mark & Sweep garbage collector.
43
# Concurrent Mark & Sweep garbage collector.
44
# (see http://wiki.netbeans.org/FaqGCPauses)
44
# (see http://wiki.netbeans.org/FaqGCPauses)
45
#
45
#
46
netbeans_default_options="-J-client -J-Xss2m -J-Xms32m -J-Dnetbeans.logger.console=true -J-ea -J-Dapple.laf.useScreenMenuBar=true -J-Dapple.awt.graphics.UseQuartz=true -J-Dsun.java2d.noddraw=true -J-Dsun.java2d.dpiaware=true -J-Dsun.zip.disableMemoryMapping=true -J-Dplugin.manager.check.updates=false -J-Dnetbeans.extbrowser.manual_chrome_plugin_install=yes"
46
netbeans_default_options="-J-client -J-Xss2m -J-Xms32m -J-Dnetbeans.logger.console=true -J-ea -J-Dapple.laf.useScreenMenuBar=true -J-Dapple.awt.graphics.UseQuartz=true -J-Dsun.java2d.noddraw=true -J-Dsun.java2d.dpiaware=true -J-Dsun.zip.disableMemoryMapping=true -J-Dnetbeans.CallbackReferences.delayShutdownOfCallbackThread=infinity -J-Dplugin.manager.check.updates=false -J-Dnetbeans.extbrowser.manual_chrome_plugin_install=yes"
47
47
48
# Default location of JDK:
48
# Default location of JDK:
49
# (set by installer or commented out if launcher should decide)
49
# (set by installer or commented out if launcher should decide)
(-)a/openide.util.lookup/arch.xml (+15 lines)
Lines 383-388 Link Here
383
    </api>
383
    </api>
384
    </li>
384
    </li>
385
    
385
    
386
    <li>
387
    <api type="export" group="systemproperty" name="netbeans.CallbackReferences.delayShutdownOfCallbackThread" category="devel">
388
        controls the shutdown of background daemon thread that process the
389
        reference callbacks. When there are no more references to process,
390
        the thread dies after the specified time in milliseconds.
391
        If the value is <code>"infinity"</code> or <code>"never"</code>,
392
        the background thread runs indefinitely and references processing
393
        is a bit more efficient. Set this property if you know that there will
394
        always be some references waiting for processing in the system.
395
        The default value is <code>0</code> - to shut down the background
396
        thread immediately when there are no more references waiting for processing.
397
        This property works since version 8.34
398
    </api>
399
    </li>
400
    
386
  </ul>
401
  </ul>
387
 </answer>
402
 </answer>
388
403
(-)a/openide.util.lookup/manifest.mf (-1 / +1 lines)
Lines 1-5 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.openide.util.lookup
2
OpenIDE-Module: org.openide.util.lookup
3
OpenIDE-Module-Localizing-Bundle: org/openide/util/lookup/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/openide/util/lookup/Bundle.properties
4
OpenIDE-Module-Specification-Version: 8.33
4
OpenIDE-Module-Specification-Version: 8.34
5
5
(-)a/openide.util.lookup/src/org/openide/util/lookup/AbstractLookup.java (-3 / +27 lines)
Lines 46-51 Link Here
46
import java.io.IOException;
46
import java.io.IOException;
47
import java.io.ObjectOutputStream;
47
import java.io.ObjectOutputStream;
48
import java.io.Serializable;
48
import java.io.Serializable;
49
import java.lang.ref.Reference;
49
import java.lang.ref.ReferenceQueue;
50
import java.lang.ref.ReferenceQueue;
50
import java.lang.ref.WeakReference;
51
import java.lang.ref.WeakReference;
51
import java.util.ArrayList;
52
import java.util.ArrayList;
Lines 61-72 Link Here
61
import java.util.Set;
62
import java.util.Set;
62
import java.util.TreeSet;
63
import java.util.TreeSet;
63
import java.util.concurrent.Executor;
64
import java.util.concurrent.Executor;
65
import java.util.concurrent.atomic.AtomicBoolean;
64
import java.util.logging.Level;
66
import java.util.logging.Level;
65
import java.util.logging.Logger;
67
import java.util.logging.Logger;
66
import org.openide.util.Lookup;
68
import org.openide.util.Lookup;
67
import org.openide.util.LookupEvent;
69
import org.openide.util.LookupEvent;
68
import org.openide.util.LookupListener;
70
import org.openide.util.LookupListener;
69
import org.openide.util.lookup.implspi.ActiveQueue;
71
import org.openide.util.lookup.implspi.ActiveQueue;
72
import org.openide.util.lookup.implspi.CallbackReferencesSupport;
70
73
71
74
72
/** Implementation of the lookup from OpenAPIs that is based on the
75
/** Implementation of the lookup from OpenAPIs that is based on the
Lines 1342-1348 Link Here
1342
1345
1343
    /** Reference to a result R
1346
    /** Reference to a result R
1344
     */
1347
     */
1345
    static final class ReferenceToResult<T> extends WeakReference<R<T>> implements Runnable {
1348
    static final class ReferenceToResult<T> extends WeakReference<R<T>> implements CallbackReferencesSupport.CallbackHandler {
1346
        /** next refernece in chain, modified only from AbstractLookup or this */
1349
        /** next refernece in chain, modified only from AbstractLookup or this */
1347
        private ReferenceToResult<?> next;
1350
        private ReferenceToResult<?> next;
1348
1351
Lines 1354-1365 Link Here
1354
1357
1355
        /** caches for results */
1358
        /** caches for results */
1356
        public Object caches;
1359
        public Object caches;
1360
        
1361
        private static final AtomicBoolean registered = new AtomicBoolean(false);
1357
1362
1358
        /** Creates a weak refernece to a new result R in context of lookup
1363
        /** Creates a weak refernece to a new result R in context of lookup
1359
         * for given template
1364
         * for given template
1360
         */
1365
         */
1361
        private ReferenceToResult(R<T> result, AbstractLookup lookup, Template<T> template) {
1366
        private ReferenceToResult(R<T> result, AbstractLookup lookup, Template<T> template) {
1362
            super(result, activeQueue());
1367
            this(CallbackReferencesSupport.getDefault(), result, lookup, template);
1368
        }
1369
        
1370
        private ReferenceToResult(CallbackReferencesSupport callbackSupport,
1371
                                  R<T> result, AbstractLookup lookup, Template<T> template) {
1372
            super(result, callbackSupport.getReferenceQueue());
1373
            callbackSupport.registerReferenceFinalizer(this);
1374
            if (!registered.getAndSet(true)) {
1375
                CallbackReferencesSupport.registerCallbackHandler(ReferenceToResult.class, new CallbackHandlerImpl());
1376
            }
1363
            this.template = template;
1377
            this.template = template;
1364
            this.lookup = lookup;
1378
            this.lookup = lookup;
1365
            getResult().reference = this;
1379
            getResult().reference = this;
Lines 1374-1380 Link Here
1374
        /** Cleans the reference. Implements Runnable interface, do not call
1388
        /** Cleans the reference. Implements Runnable interface, do not call
1375
         * directly.
1389
         * directly.
1376
         */
1390
         */
1377
        public void run() {
1391
        @Override
1392
        public void handleCallback(Reference reference) {
1378
            lookup.cleanUpResult(this.template);
1393
            lookup.cleanUpResult(this.template);
1379
        }
1394
        }
1380
1395
Lines 1399-1404 Link Here
1399
        private ReferenceToResult<T> cloneRef() {
1414
        private ReferenceToResult<T> cloneRef() {
1400
            return new ReferenceToResult<T>(getResult(), lookup, template);
1415
            return new ReferenceToResult<T>(getResult(), lookup, template);
1401
        }
1416
        }
1417
        
1418
        private static final class CallbackHandlerImpl implements CallbackReferencesSupport.CallbackHandler {
1419
1420
            @Override
1421
            public void handleCallback(Reference reference) {
1422
                ((ReferenceToResult) reference).handleCallback(reference);
1423
            }
1424
            
1425
        }
1402
    }
1426
    }
1403
     // end of ReferenceToResult
1427
     // end of ReferenceToResult
1404
1428
(-)a/openide.util.lookup/src/org/openide/util/lookup/implspi/ActiveQueue.java (-15 / +38 lines)
Lines 46-51 Link Here
46
import java.lang.ref.ReferenceQueue;
46
import java.lang.ref.ReferenceQueue;
47
import java.util.logging.Level;
47
import java.util.logging.Level;
48
import java.util.logging.Logger;
48
import java.util.logging.Logger;
49
import org.openide.util.lookup.implspi.CallbackReferencesSupport.CallbackHandler;
49
50
50
/**
51
/**
51
 * Implementation of the active reference queue.
52
 * Implementation of the active reference queue.
Lines 129-151 Link Here
129
                        return;
130
                        return;
130
                    }
131
                    }
131
                    Reference<?> ref = impl.removeSuper();
132
                    Reference<?> ref = impl.removeSuper();
132
                    LOGGER.log(Level.FINE, "Got dequeued reference {0}", new Object[] { ref });
133
                    LOGGER.log(Level.FINE, "Got dequeued reference {0}", ref);
133
                    if (!(ref instanceof Runnable)) {
134
                    CallbackHandler callbackHandler = CallbackReferencesSupport.getCallbackHandler(ref);
134
                        LOGGER.log(Level.WARNING, "A reference not implementing runnable has been added to the Utilities.activeReferenceQueue(): {0}", ref.getClass());
135
                    Runnable run = null;
135
                        continue;
136
                    if (callbackHandler == null) {
137
                        if (!(ref instanceof Runnable)) {
138
                            LOGGER.log(Level.WARNING, "An unexpected reference in the queue: {0}", ref.getClass());
139
                            continue;
140
                        } else {
141
                            run = (Runnable) ref;
142
                        }
136
                    }
143
                    }
137
                    // do the cleanup
144
                    // do the cleanup
138
                    try {
145
                    if (callbackHandler != null) {
139
                        ((Runnable) ref).run();
146
                        try {
140
                    } catch (ThreadDeath td) {
147
                            callbackHandler.handleCallback(ref);
141
                        throw td;
148
                        } catch (ThreadDeath td) {
142
                    } catch (Throwable t) {
149
                            throw td;
143
                        // Should not happen.
150
                        } catch (Throwable t) {
144
                        // If it happens, it is a bug in client code, notify!
151
                            // Should not happen.
145
                        LOGGER.log(Level.WARNING, "Cannot process " + ref, t);
152
                            // If it happens, it is a bug in client code, notify!
146
                    } finally {
153
                            LOGGER.log(Level.WARNING, "Cannot process " + ref, t);
147
                        // to allow GC
154
                        } finally {
148
                        ref = null;
155
                            // to allow GC
156
                            ref = null;
157
                            callbackHandler = null;
158
                        }
159
                    } else if (run != null) {
160
                        try {
161
                            ((Runnable) ref).run();
162
                        } catch (ThreadDeath td) {
163
                            throw td;
164
                        } catch (Throwable t) {
165
                            // Should not happen.
166
                            // If it happens, it is a bug in client code, notify!
167
                            LOGGER.log(Level.WARNING, "Cannot process " + ref, t);
168
                        } finally {
169
                            // to allow GC
170
                            ref = null;
171
                        }
149
                    }
172
                    }
150
                } catch (InterruptedException ex) {
173
                } catch (InterruptedException ex) {
151
                    // Can happen during VM shutdown, it seems. Ignore.
174
                    // Can happen during VM shutdown, it seems. Ignore.
(-)a/openide.util.lookup/src/org/openide/util/lookup/implspi/CallbackReferencesSupport.java (+180 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.openide.util.lookup.implspi;
43
44
import java.lang.ref.Reference;
45
import java.lang.ref.ReferenceQueue;
46
import java.util.HashMap;
47
import java.util.Map;
48
import java.util.logging.Logger;
49
50
/**
51
 * Support class for building callback references. Callback references are able
52
 * to execute a dedicated callback when the object the reference refers to, becomes unreachable.
53
 * <p>
54
 * Do not use this class, unless you know well what you're doing. Use rather
55
 * <code>org.openide.util.CallbackReferences</code> instead.
56
 * <p>
57
 * <b>Usage:</b>
58
 * <ol>
59
 * <li>Get the default implementation of this support from {@link CallbackReferencesSupport#getDefault()}</li>
60
 * <li>Create the desired reference with {@link #getReferenceQueue()}.
61
 *     It's important that there is one reference instance per one
62
 *     CallbackReferencesSupport instance.</li>
63
 * <li>Register the reference for finalization by calling
64
 *     {@link #registerReferenceFinalizer(java.lang.ref.Reference)} method.</li>
65
 * <li>Register the reference class with the callback handler via
66
 *     {@link #registerCallbackHandler(java.lang.Class, org.openide.util.lookup.implspi.CallbackReferencesSupport.CallbackHandler)}</li>
67
 * <li>It's important that there is exactly one call to {@link #getReferenceQueue()}
68
 *     and {@link #registerReferenceFinalizer(java.lang.ref.Reference)} per
69
 *     a reference instance.</li>
70
 * </ol>
71
 * 
72
 * @author Martin Entlicher
73
 * @since 8.34
74
 * @see org.openide.util.CallbackReferences
75
 */
76
public abstract class CallbackReferencesSupport {
77
    
78
    static final Logger LOGGER = Logger.getLogger(CallbackReferencesSupportFinishable.class.getName());
79
    
80
    static final long CALLBACK_THREAD_SHUTDOWN_DELAY = getCallbackThreadShutdownDelay();
81
    private static final boolean INDEFINITE_CALLBACK_THREAD = CALLBACK_THREAD_SHUTDOWN_DELAY == Long.MAX_VALUE;
82
    private static final CallbackReferencesSupport DEFAULT = createDefault();
83
    static final Map<Class<? extends Reference>, CallbackHandler> callbackHandlers = new HashMap<Class<? extends Reference>, CallbackHandler>();
84
85
    CallbackReferencesSupport() {}
86
    
87
    private static long getCallbackThreadShutdownDelay() {
88
        String prop = System.getProperty("netbeans.CallbackReferences.delayShutdownOfCallbackThread");  // NOI18N
89
        if (prop == null) {
90
            return 0;   // Shutdown immediately
91
        }
92
        prop = prop.toLowerCase();
93
        if ("never".equals(prop) || "infinity".equals(prop) || "inf".equals(prop)) {    // NOI18N
94
            return Long.MAX_VALUE;
95
        }
96
        try {
97
            return Long.decode(prop);
98
        } catch (NumberFormatException e) {
99
            return 0;
100
        }
101
    }
102
    
103
    private static CallbackReferencesSupport createDefault() {
104
        if (INDEFINITE_CALLBACK_THREAD) {
105
            return new CallbackReferencesSupportIndefinite();
106
        } else {
107
            return new CallbackReferencesSupportFinishable();
108
        }
109
    }
110
    
111
    /**
112
     * Get the default implementation of {@link CallbackReferencesSupport}.
113
     * @return The default {@link CallbackReferencesSupport}.
114
     */
115
    public static CallbackReferencesSupport getDefault() {
116
        return DEFAULT;
117
    }
118
    
119
    /**
120
     * Register a callback handler associated with the reference class type.
121
     * @param refClass The class type of the reference.
122
     * @param callbackHandler The handler of the reference callback.
123
     */
124
    public static void registerCallbackHandler(Class<? extends Reference> refClass, CallbackHandler callbackHandler) {
125
        synchronized (callbackHandlers) {
126
            callbackHandlers.put(refClass, callbackHandler);
127
        }
128
    }
129
    /**
130
     * Get the reference queue, solely for the purpose of reference creation.
131
     * @return The reference queue.
132
     */
133
    public abstract ReferenceQueue getReferenceQueue();
134
    
135
    /**
136
     * Be sure to call this method for every instance of this class,
137
     * to assure that the finalization of the associated reference is correctly
138
     * handled.
139
     * @param ref The associated reference
140
     */
141
    public abstract void registerReferenceFinalizer(Reference ref);
142
    
143
    static final CallbackHandler getCallbackHandler(Reference ref) {
144
        Class refClass = ref.getClass();
145
        CallbackHandler callbackHandler = null;
146
        synchronized (callbackHandlers) {
147
            for (Map.Entry<Class<? extends Reference>, CallbackHandler> entry : callbackHandlers.entrySet()) {
148
                if (entry.getKey().isAssignableFrom(refClass)) {
149
                    callbackHandler = entry.getValue();
150
                    break;
151
                }
152
            }
153
        }
154
        return callbackHandler;
155
    }
156
    
157
    final void cleanup(Reference ref) {
158
        CallbackHandler callbackHandler = getCallbackHandler(ref);
159
        if (callbackHandler != null) {
160
            callbackHandler.handleCallback(ref);
161
        } else {
162
            LOGGER.warning("No callback handler for reference "+ref+" of class "+ref.getClass()); // NOI18N
163
        }
164
    }
165
166
    /**
167
     * Handler of a reference callback for the given {@link Reference}.
168
     * @see #registerCallbackHandler(java.lang.Class, org.openide.util.lookup.implspi.CallbackReferencesSupport.CallbackHandler)
169
     */
170
    public static interface CallbackHandler {
171
        
172
        /**
173
         * Perform the callback for the given reference.
174
         * @param ref The reference
175
         */
176
        void handleCallback(Reference ref);
177
        
178
    }
179
    
180
}
(-)a/openide.util.lookup/src/org/openide/util/lookup/implspi/CallbackReferencesSupportFinishable.java (+336 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.openide.util.lookup.implspi;
43
44
import java.lang.ref.Reference;
45
import java.lang.ref.ReferenceQueue;
46
import java.lang.ref.WeakReference;
47
import java.util.Collections;
48
import java.util.HashMap;
49
import java.util.Iterator;
50
import java.util.LinkedList;
51
import java.util.Map;
52
import java.util.logging.Level;
53
54
/**
55
 * Callback reference support with a finishable background thread.
56
 * 
57
 * @author martin
58
 */
59
final class CallbackReferencesSupportFinishable extends CallbackReferencesSupport {
60
    
61
    private long activeRefsCount = 0l;
62
    private final Object LOCK = new Object();
63
    
64
    private ReferenceQueue callbackRefQueue;  // Current callback reference queue
65
    private final Map<Object, FinalizingReference> finalizingReferences = Collections.synchronizedMap(new HashMap<Object, FinalizingReference>());
66
    private Daemon running;
67
    
68
    CallbackReferencesSupportFinishable() {
69
    }
70
    
71
    /**
72
     * Get the reference queue, solely for the purpose of reference creation.
73
     * @return The reference queue.
74
     */
75
    @Override
76
    public ReferenceQueue getReferenceQueue() {
77
        ReferenceQueue rq;
78
        synchronized (LOCK) {
79
            activeRefsCount++;  // The queue is retrieved for a single reference
80
            rq = callbackRefQueue;
81
            if (rq == null) {
82
                rq = new ReferenceQueue();
83
                callbackRefQueue = rq;
84
                pingDaemon();
85
            }
86
        }
87
        return rq;
88
    }
89
    
90
    /**
91
     * Be sure to call this method for every instance of this class,
92
     * to assure that the finalization of the associated reference is correctly
93
     * handled.
94
     * @param ref The associated reference
95
     */
96
    @Override
97
    public void registerReferenceFinalizer(Reference ref) {
98
        FinalizingReference fr = new FinalizingReference(ref, getReferenceQueue());
99
        if (LOGGER.isLoggable(Level.FINE)) {
100
            synchronized (LOCK) {
101
                LOGGER.log(Level.FINE, "new FinalizingReference: activeRefsCount = {0}", activeRefsCount);
102
            }
103
        }
104
        finalizingReferences.put(fr, fr);
105
    }
106
107
    private void died() {
108
        boolean isLoggable = LOGGER.isLoggable(Level.FINE);
109
        synchronized (LOCK) {
110
            if ((activeRefsCount -= 2) == 0) {
111
                callbackRefQueue = null;
112
                if (running != null) {
113
                    running.doFinish();
114
                }
115
            }
116
            if (isLoggable) {
117
                LOGGER.log(Level.FINE, "DIED: activeRefsCount = {0}", activeRefsCount);
118
            }
119
        }
120
    }
121
    
122
    private static final class FinalizingReference extends WeakReference<Reference> {
123
        
124
        final int hashCode;
125
        
126
        public FinalizingReference(Reference referent, ReferenceQueue rq) {
127
            super(referent, rq);
128
            this.hashCode = System.identityHashCode(referent);
129
        }
130
131
        @Override
132
        public int hashCode() {
133
            return hashCode;
134
        }
135
136
        @Override
137
        public boolean equals(Object obj) {
138
            if (obj instanceof FinalizingReference) {
139
                FinalizingReference other = (FinalizingReference) obj;
140
                if (hashCode != other.hashCode) {
141
                    return false;
142
                }
143
                return get() == other.get();
144
            } else if (obj instanceof ReferenceKey) {
145
                Reference referent = get();
146
                ReferenceKey rk = (ReferenceKey) obj;
147
                return rk.compareToNullRef && referent == null || referent == rk.ref;
148
            } else if (obj instanceof FinalizingKey) {
149
                return this == ((FinalizingKey) obj).fr;
150
            }
151
            return false;
152
        }
153
        
154
    }
155
    
156
    private static class ReferenceKey {
157
        
158
        private final int hashCode;
159
        final Reference ref;
160
        final boolean compareToNullRef;
161
        
162
        public ReferenceKey(Reference ref, boolean compareToNullRef) {
163
            this.ref = ref;
164
            this.hashCode = System.identityHashCode(ref);
165
            this.compareToNullRef = compareToNullRef;
166
        }
167
168
        @Override
169
        public int hashCode() {
170
            return hashCode;
171
        }
172
173
        @Override
174
        public boolean equals(Object obj) {
175
            if (obj instanceof FinalizingReference) {
176
                FinalizingReference fr = (FinalizingReference) obj;
177
                if (fr.hashCode != hashCode) {
178
                    return false;
179
                }
180
                Reference frRef = fr.get();
181
                return compareToNullRef && frRef == null || frRef == ref;
182
            }
183
            return false;
184
        }
185
    }
186
    
187
    private static final class FinalizingKey {
188
        
189
        final FinalizingReference fr;
190
        private final int hashCode;
191
        
192
        public FinalizingKey(FinalizingReference fr) {
193
            this.fr = fr;
194
            this.hashCode = fr.hashCode;
195
        }
196
197
        @Override
198
        public int hashCode() {
199
            return hashCode;
200
        }
201
202
        @Override
203
        public boolean equals(Object obj) {
204
            return obj == fr;
205
        }
206
    }
207
    
208
    static boolean isDaemonRunning() {
209
        CallbackReferencesSupportFinishable support = (CallbackReferencesSupportFinishable) CallbackReferencesSupport.getDefault();
210
        synchronized (support.LOCK) {
211
            return support.running != null;
212
        }
213
    }
214
    
215
    private void pingDaemon() {
216
        synchronized (LOCK) {
217
            if (running == null) {
218
                Daemon t = new Daemon();
219
                t.setPriority(Thread.MIN_PRIORITY);
220
                t.setDaemon(true);
221
                t.start();
222
                LOGGER.fine("starting thread");
223
                running = t;
224
            } else {
225
                LOCK.notifyAll();
226
            }
227
        }
228
    }
229
    
230
    private final class Daemon extends Thread {
231
        
232
        public Daemon() {
233
            super("Cleanable Reference Queue Daemon");      // NOI18N
234
        }
235
236
        private void doFinish() {
237
            interrupt();
238
        }
239
        
240
        @Override
241
        public void run() {
242
            final LinkedList<FinalizingReference> postponedFinalizingReferences = new LinkedList<FinalizingReference>();
243
            Reference<?> nextRef = null;
244
            while (true) {
245
                try {
246
                    ReferenceQueue rq;
247
                    synchronized (LOCK) {
248
                        rq = callbackRefQueue;
249
                        if (rq == null) {
250
                            assert finalizingReferences.isEmpty() : finalizingReferences.toString();
251
                            assert postponedFinalizingReferences.isEmpty() : postponedFinalizingReferences.toString();
252
                            if (CALLBACK_THREAD_SHUTDOWN_DELAY > 0) {
253
                                LOCK.wait(CALLBACK_THREAD_SHUTDOWN_DELAY);
254
                                rq = callbackRefQueue;
255
                            }
256
                            if (rq == null) {
257
                                LOGGER.fine("Daemon DIES.");
258
                                running = null;
259
                                return ;
260
                            }
261
                        }
262
                    }
263
                    boolean fromQueue = false;
264
                    Reference<?> ref = nextRef;
265
                    nextRef = null;
266
                    if (ref == null) {
267
                        ref = rq.poll();
268
                    }
269
                    if (!postponedFinalizingReferences.isEmpty() && ref == null) {
270
                        ref = postponedFinalizingReferences.removeFirst();
271
                    } else {
272
                        if (ref == null) {
273
                            ref = rq.remove();
274
                        }
275
                        fromQueue = true;
276
                    }
277
                    LOGGER.log(Level.FINE, "Got {0}", new Object[] { ref });
278
                    boolean finalizing = ref instanceof FinalizingReference;
279
                    if (finalizing) {
280
                        FinalizingKey finalizingKey = new FinalizingKey((FinalizingReference) ref);
281
                        if (fromQueue && finalizingReferences.containsKey(finalizingKey)) {
282
                            nextRef = rq.poll();
283
                            if (nextRef != null) {
284
                                // There are other items in the queue, process them before finalizing references:
285
                                postponedFinalizingReferences.add((FinalizingReference) ref);
286
                                continue;
287
                            }
288
                        }
289
                        FinalizingReference removed = finalizingReferences.remove(finalizingKey);
290
                        if (removed != null) {
291
                            // No callback, the reference was collected
292
                            died();
293
                        } else {
294
                            // The reference was collected, but the callback was processed already.
295
                        }
296
                    } else {
297
                        FinalizingReference removed = finalizingReferences.remove(new ReferenceKey(ref, false));
298
                        if (removed == null) {
299
                            removed = finalizingReferences.remove(new ReferenceKey(ref, true));
300
                        }
301
                        if (removed != null) {
302
                            // Remove from postponedFinalizingReferences list if it happens to be there
303
                            // Need to manually iterate through, because of the necessary identity comparisons:
304
                            Iterator<FinalizingReference> frIt = postponedFinalizingReferences.iterator();
305
                            while (frIt.hasNext()) {
306
                                if (frIt.next() == removed) {
307
                                    frIt.remove();
308
                                    break;
309
                                }
310
                            }
311
                            // do the cleanup
312
                            try {
313
                                cleanup(ref);
314
                            } catch (ThreadDeath td) {
315
                                throw td;
316
                            } catch (Throwable t) {
317
                                // Should not happen.
318
                                // If it happens, it is a bug in client code, notify!
319
                                LOGGER.log(Level.WARNING, "Cannot process " + ref, t);
320
                            }
321
                            removed.clear();
322
                            died();
323
                        } else {
324
                            LOGGER.log(Level.WARNING, "Finalizing reference missing.",
325
                                       new IllegalStateException("No finalizing reference for "+ref));
326
                        }
327
                    }
328
                } catch (InterruptedException ex) {
329
                    // Can happen during VM shutdown, or by finish call.
330
                    continue;
331
                }
332
            }
333
        }
334
    }
335
336
}
(-)a/openide.util.lookup/src/org/openide/util/lookup/implspi/CallbackReferencesSupportIndefinite.java (+67 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.openide.util.lookup.implspi;
43
44
import java.lang.ref.Reference;
45
import java.lang.ref.ReferenceQueue;
46
47
/**
48
 * Callback reference support with an indefinite background thread.
49
 * 
50
 * @author martin
51
 */
52
class CallbackReferencesSupportIndefinite extends CallbackReferencesSupport {
53
    
54
    CallbackReferencesSupportIndefinite() {
55
    }
56
57
    @Override
58
    public ReferenceQueue getReferenceQueue() {
59
        return ActiveQueue.queue();
60
    }
61
62
    @Override
63
    public void registerReferenceFinalizer(Reference ref) {
64
        // No need for finalizers
65
    }
66
67
}
(-)a/openide.util.lookup/test/unit/src/org/openide/util/lookup/LookupPermGenLeakTest.java (-1 / +1 lines)
Lines 132-138 Link Here
132
    
132
    
133
    public void testClassLoaderCanGC() throws Exception {
133
    public void testClassLoaderCanGC() throws Exception {
134
        Reference<?> ref = new WeakReference<Object>(createClass());
134
        Reference<?> ref = new WeakReference<Object>(createClass());
135
        // assertGC("Can be GCed", ref); TODO: Uncomment after #257013 is implemented.
135
        assertGC("Can be GCed", ref);
136
    }
136
    }
137
    
137
    
138
    private synchronized int waitForOne() throws InterruptedException {
138
    private synchronized int waitForOne() throws InterruptedException {
(-)a/openide.util.lookup/test/unit/src/org/openide/util/lookup/implspi/CallbackReferencesSupportDelayedTest.java (+91 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.openide.util.lookup.implspi;
43
44
import java.lang.ref.Reference;
45
import static junit.framework.TestCase.assertFalse;
46
import static junit.framework.TestCase.assertTrue;
47
import org.netbeans.junit.NbTestCase;
48
49
/**
50
 *
51
 * @author martin
52
 */
53
public class CallbackReferencesSupportDelayedTest extends NbTestCase {
54
    
55
    public CallbackReferencesSupportDelayedTest(String testName) {
56
        super(testName);
57
    }
58
59
    public void testDaemonShutdownDelay() throws InterruptedException {
60
        int delay = 5000;
61
        System.setProperty("netbeans.CallbackReferences.delayShutdownOfCallbackThread", Integer.toString(delay));
62
        int repeats = 3;
63
        while (repeats-- > 0) {
64
            new CallbackReferencesSupportTest.TestWeak(new Object(), new CallbackReferencesSupportTest.SupportCallback() {
65
                @Override public void referenceCleared(Reference ref) {}
66
            });
67
            long startTime = System.currentTimeMillis();
68
            CallbackReferencesSupportFinishable crsf = (CallbackReferencesSupportFinishable) CallbackReferencesSupport.getDefault();
69
            boolean active = crsf.isDaemonRunning();
70
            assertTrue(active);
71
            long endTime = startTime + delay - 200;
72
            long cTime = System.currentTimeMillis();
73
            int i;
74
            for (i = 0; crsf.isDaemonRunning() && cTime < endTime; i++) {
75
                System.gc();
76
                Thread.sleep(50);
77
                cTime = System.currentTimeMillis();
78
            }
79
            active = crsf.isDaemonRunning();
80
            assertTrue("Daemon should not die sooner than "+delay+", died after "+(cTime - startTime)+"ms", active);
81
            for (i = 0; i < 10 && crsf.isDaemonRunning(); i++) {
82
                System.gc();
83
                Thread.sleep(50);
84
            }
85
            active = crsf.isDaemonRunning();
86
            assertFalse("Daemon should die after "+delay, active);
87
        }
88
        System.clearProperty("netbeans.CallbackReferences.delayShutdownOfCallbackThread");
89
    }
90
    
91
}
(-)a/openide.util.lookup/test/unit/src/org/openide/util/lookup/implspi/CallbackReferencesSupportIndefiniteTest.java (+68 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.openide.util.lookup.implspi;
43
44
import java.lang.ref.Reference;
45
import java.lang.ref.ReferenceQueue;
46
import org.netbeans.junit.NbTestCase;
47
48
/**
49
 *
50
 * @author martin
51
 */
52
public class CallbackReferencesSupportIndefiniteTest extends NbTestCase {
53
    
54
    public CallbackReferencesSupportIndefiniteTest(String testName) {
55
        super(testName);
56
    }
57
    
58
    public void testDaemonNeverShutdown() throws InterruptedException {
59
        System.setProperty("netbeans.CallbackReferences.delayShutdownOfCallbackThread", "infinity");
60
        new CallbackReferencesSupportTest.TestWeak(new Object(), new CallbackReferencesSupportTest.SupportCallback() {
61
            @Override public void referenceCleared(Reference ref) {}
62
        });
63
        assertTrue(CallbackReferencesSupport.getDefault() instanceof CallbackReferencesSupportIndefinite);
64
        ReferenceQueue rq = CallbackReferencesSupport.getDefault().getReferenceQueue();
65
        assertEquals(ActiveQueue.queue(), rq);
66
        System.clearProperty("netbeans.CallbackReferences.delayShutdownOfCallbackThread");
67
    }
68
}
(-)a/openide.util.lookup/test/unit/src/org/openide/util/lookup/implspi/CallbackReferencesSupportTest.java (+445 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.openide.util.lookup.implspi;
43
44
import java.lang.ref.Reference;
45
import java.lang.ref.ReferenceQueue;
46
import java.lang.ref.WeakReference;
47
import java.util.LinkedHashMap;
48
import java.util.Map;
49
import java.util.concurrent.atomic.AtomicBoolean;
50
import java.util.logging.Handler;
51
import java.util.logging.Level;
52
import java.util.logging.LogRecord;
53
import java.util.logging.Logger;
54
import org.netbeans.junit.Log;
55
import org.netbeans.junit.NbTestCase;
56
import org.openide.util.lookup.implspi.CallbackReferencesSupport.CallbackHandler;
57
58
/**
59
 *
60
 * @author martin
61
 */
62
public class CallbackReferencesSupportTest extends NbTestCase {
63
    
64
    private LoggerWarningsHandler logHandler;
65
    
66
    public CallbackReferencesSupportTest(String testName) {
67
        super(testName);
68
    }
69
70
    @Override
71
    protected void setUp() throws Exception {
72
        Logger log = Logger.getLogger(CallbackReferencesSupport.class.getName());
73
        Log.enable(log.getName(), Level.FINEST);
74
        logHandler = new LoggerWarningsHandler();
75
        log.addHandler(logHandler);
76
    }
77
78
    @Override
79
    protected void tearDown() throws Exception {
80
        assertFalse(logHandler.toString(), logHandler.hasMessages());
81
        Logger log = Logger.getLogger(CallbackReferencesSupport.class.getName());
82
        log.removeHandler(logHandler);
83
        logHandler = null;
84
    }
85
    
86
    public void testCleanupCallback() throws Exception {
87
        final boolean[] cleared = new boolean[] { false };
88
        final boolean[] finalized = new boolean[] { false };
89
        SupportCallback callback = new SupportCallback() {
90
            @Override
91
            public void referenceCleared(Reference ref) {
92
                synchronized (cleared) {
93
                    cleared[0] = true;
94
                    cleared.notifyAll();
95
                }
96
            }
97
        };
98
        
99
        // Hold the reference and free the referent:
100
        System.err.println("Hold the reference and free the referent");
101
        Object obj = new Object();
102
        TestWeakWithFinalize twf = new TestWeakWithFinalize(obj, callback, finalized);
103
        CallbackReferencesSupportFinishable crsf = (CallbackReferencesSupportFinishable) CallbackReferencesSupport.getDefault();
104
        boolean active = crsf.isDaemonRunning();
105
        assertTrue("The daemon started", active);
106
        WeakReference objRef = new WeakReference(obj);
107
        obj = null;
108
        assertGC("Object should be GC'ed", objRef);
109
        synchronized (cleared) {
110
            while (!cleared[0]) {
111
                cleared.wait(100);
112
                System.gc();
113
            }
114
        }
115
        assertTrue(cleared[0]);
116
        assertFalse(finalized[0]);
117
        active = crsf.isDaemonRunning();
118
        assertFalse("The daemon should finish", active);
119
        WeakReference twRef = new WeakReference(twf);
120
        twf = null;
121
        assertGC("TestWeak reference should be GC'ed", twRef);
122
        synchronized (finalized) {
123
            while (!finalized[0]) {
124
                finalized.wait(100);
125
                System.gc();
126
            }
127
        }
128
        assertTrue(finalized[0]);
129
        // Reset:
130
        cleared[0] = false;
131
        finalized[0] = false;
132
        
133
        // Free the reference and hold the referent
134
        System.err.println("Free the reference and hold the referent");
135
        obj = new Object();
136
        twf = new TestWeakWithFinalize(obj, callback, finalized);
137
        active = crsf.isDaemonRunning();
138
        assertTrue("The daemon started", active);
139
        twRef = new WeakReference(twf);
140
        twf = null;
141
        assertGC("TestWeak reference should be GC'ed", twRef);
142
        synchronized (finalized) {
143
            while (!finalized[0]) {
144
                finalized.wait(100);
145
                System.gc();
146
            }
147
        }
148
        assertTrue(finalized[0]);
149
        assertFalse(cleared[0]); // Should never clear
150
        objRef = new WeakReference(obj);
151
        obj = null;
152
        assertGC("Object should be GC'ed", objRef);
153
        assertFalse(cleared[0]); // Should never clear
154
        while (crsf.isDaemonRunning()) {
155
            System.gc();
156
            Thread.sleep(100);
157
        }
158
        active = crsf.isDaemonRunning();
159
        assertFalse("The daemon should finish", active);
160
        // Reset:
161
        cleared[0] = false;
162
        finalized[0] = false;
163
        
164
        // Free both the reference and the referent
165
        System.err.println("Free both the reference and the referent");
166
        obj = new Object();
167
        TestWeak tw = new TestWeak(obj, callback);
168
        active = crsf.isDaemonRunning();
169
        assertTrue("The daemon started", active);
170
        objRef = new WeakReference(obj);
171
        obj = null;
172
        twRef = new WeakReference(tw);
173
        tw = null;
174
        assertGC("Object should be GC'ed", objRef);
175
        assertGC("TestWeak reference should be GC'ed", twRef);
176
        
177
        while (crsf.isDaemonRunning()) {
178
            System.gc();
179
            Thread.sleep(100);
180
        }
181
        
182
        // Free several referents and hold the references
183
        System.err.println("Free several referents and hold the references");
184
        int n = 500000;
185
        Object[] objs = new Object[n];
186
        TestWeak[] tws = new TestWeak[n];
187
        final boolean[] calledBack = new boolean[n];
188
        SupportCallback cb = new SupportCallback() {
189
            @Override public void referenceCleared(Reference ref) {
190
                calledBack[((TestWeak) ref).id] = true;
191
            }
192
        };
193
        for (int i = 0; i < n; i++) {
194
            objs[i] = new Object();
195
            tws[i] = new TestWeak(objs[i], cb);
196
            tws[i].id = i;
197
        }
198
        active = crsf.isDaemonRunning();
199
        assertTrue("The daemon started", active);
200
        WeakReference[] objRefs = new WeakReference[n];
201
        WeakReference[] twRefs = new WeakReference[n];
202
        for (int i = 0; i < n; i++) {
203
            objRefs[i] = new WeakReference(objs[i]);
204
            twRefs[i] = new WeakReference(tws[i]);
205
        }
206
        objs = null;
207
        for (int i = 0; i < n; i++) {
208
            assertGC("Object "+i+" should be GC'ed", objRefs[i]);
209
            assertEquals(tws[i], twRefs[i].get());
210
        }
211
        
212
        while (crsf.isDaemonRunning()) {
213
            System.gc();
214
            Thread.sleep(100);
215
        }
216
        String ncb = null;
217
        for (int i = 0; i < n; i++) {
218
            if (!calledBack[i]) {
219
                if (ncb == null) {
220
                    ncb = Integer.toString(i);
221
                } else {
222
                    ncb += ", "+i;
223
                }
224
            }
225
        }
226
        assertNull("Not called back: "+ncb, ncb);
227
        tws = null;
228
        for (int i = 0; i < n; i++) {
229
            assertGC("TestWeak reference "+i+" should be GC'ed", twRefs[i]);
230
        }
231
        
232
        // Free several references and the referents
233
        System.err.println("Free several references and the referents");
234
        objs = new Object[n];
235
        tws = new TestWeak[n];
236
        for (int i = 0; i < n; i++) {
237
            objs[i] = new Object();
238
            tws[i] = new TestWeak(objs[i], cb);
239
            tws[i].id = i;
240
        }
241
        active = crsf.isDaemonRunning();
242
        assertTrue("The daemon started", active);
243
        objRefs = new WeakReference[n];
244
        twRefs = new WeakReference[n];
245
        for (int i = 0; i < n; i++) {
246
            objRefs[i] = new WeakReference(objs[i]);
247
            twRefs[i] = new WeakReference(tws[i]);
248
        }
249
        objs = null;
250
        tws = null;
251
        for (int i = 0; i < n; i++) {
252
            assertGC("Object "+i+" should be GC'ed", objRefs[i]);
253
            assertGC("TestWeak reference "+i+" should be GC'ed", twRefs[i]);
254
        }
255
        
256
        while (crsf.isDaemonRunning()) {
257
            System.gc();
258
            Thread.sleep(100);
259
        }
260
    }
261
    
262
    public void testNullRefs() throws Exception {
263
        final boolean[] cleared = new boolean[] { false };
264
        final boolean[] finalized = new boolean[] { false };
265
        /*CallbackReferencesSupport support = CallbackReferencesSupport.create(new SupportCallback() {
266
            @Override
267
            public void referenceCleared(Reference ref) {
268
                cleared[0] = true;
269
            }
270
        });*/
271
        CallbackReferencesSupport support = CallbackReferencesSupport.getDefault();
272
        new WeakReferenceOnNull(support, finalized);
273
        CallbackReferencesSupportFinishable crsf = (CallbackReferencesSupportFinishable) CallbackReferencesSupport.getDefault();
274
        boolean active = crsf.isDaemonRunning();
275
        // is probably active, unless the WeakReferenceOnNull was already finalized.
276
        synchronized (finalized) {
277
            while (!finalized[0]) {
278
                finalized.wait(100);
279
                System.gc();
280
            }
281
        }
282
        assertTrue("The weak ref was not finalized?!?", finalized[0]);
283
        //WeakReference supportRef = new WeakReference(support);
284
        //support = null;
285
        //assertGC("The support should be GC'ed", supportRef);
286
        // Since the ref was null, the clearing callback was not called:
287
        assertFalse("Unexpectedly cleared!", cleared[0]);
288
        while (crsf.isDaemonRunning()) {
289
            System.gc();
290
            Thread.sleep(100);
291
        }
292
        active = crsf.isDaemonRunning();
293
        assertFalse("The daemon shoud die!", active);
294
        /*
295
        boolean isNPE = false;
296
        try {
297
            new TestWeak(new Object(), (SupportCallback) null, null);
298
        } catch (NullPointerException npex) {
299
            isNPE = true;
300
        }
301
        assertTrue(isNPE);
302
        */
303
        active = crsf.isDaemonRunning();
304
        assertFalse("NPE should not start the daemon", active);
305
    }
306
    
307
    private static class WeakReferenceOnNull extends WeakReference {
308
        
309
        private final CallbackReferencesSupport support;
310
        private final boolean[] finalized;
311
        
312
        public WeakReferenceOnNull(CallbackReferencesSupport support, boolean[] finalized) {
313
            super(null, support.getReferenceQueue());
314
            this.support = support;
315
            support.registerReferenceFinalizer(this);
316
            this.finalized = finalized;
317
        }
318
        
319
        @Override
320
        protected void finalize() throws Throwable {
321
            synchronized (finalized) {
322
                finalized[0] = true;
323
                finalized.notifyAll();
324
            }
325
            super.finalize();
326
            //support.notifyFinalized();
327
        }
328
    }
329
    
330
    static class TestWeak<T> extends WeakReference<T> {
331
        
332
        private static final AtomicBoolean REGISTERED = new AtomicBoolean(false);
333
        
334
        final SupportCallback cleanup;
335
        int id;
336
        
337
        public TestWeak(T referent, SupportCallback cleanup) {
338
            this(referent, cleanup, CallbackReferencesSupport.getDefault());
339
        }
340
        
341
        private TestWeak(T referent, SupportCallback cleanup, CallbackReferencesSupport support) {
342
            super(referent, support.getReferenceQueue());
343
            this.cleanup = cleanup;
344
            support.registerReferenceFinalizer(this);
345
            if (!REGISTERED.getAndSet(true)) {
346
                CallbackReferencesSupport.registerCallbackHandler(TestWeak.class, new TestCallbackHandler());
347
            }
348
        }
349
        
350
        private static final class TestCallbackHandler implements CallbackHandler {
351
352
            @Override
353
            public void handleCallback(Reference ref) {
354
                ((TestWeak) ref).cleanup.referenceCleared(ref);
355
            }
356
            
357
        }
358
        
359
    }
360
361
    static class TestWeakWithFinalize<T> extends WeakReference<T> {
362
        
363
        private static final AtomicBoolean REGISTERED = new AtomicBoolean(false);
364
        
365
        final SupportCallback cleanup;
366
        private final boolean[] finalized;
367
        int id;
368
        
369
        public TestWeakWithFinalize(T referent, SupportCallback cleanup, boolean[] finalized) {
370
            this(referent, cleanup, CallbackReferencesSupport.getDefault(), finalized);
371
        }
372
        
373
        private TestWeakWithFinalize(T referent, SupportCallback cleanup, CallbackReferencesSupport support, boolean[] finalized) {
374
            super(referent, support.getReferenceQueue());
375
            this.cleanup = cleanup;
376
            support.registerReferenceFinalizer(this);
377
            this.finalized = finalized;
378
            if (!REGISTERED.getAndSet(true)) {
379
                CallbackReferencesSupport.registerCallbackHandler(TestWeakWithFinalize.class, new TestCallbackHandler());
380
            }
381
        }
382
        
383
        @Override
384
        protected final void finalize() throws Throwable {
385
            //System.err.println("TestWeak finzalizing...");
386
            //support.notifyFinalized();
387
            super.finalize();
388
            if (finalized != null) {
389
                synchronized (finalized) {
390
                    finalized[0] = true;
391
                    finalized.notifyAll();
392
                }
393
            }
394
        }
395
        
396
        private static final class TestCallbackHandler implements CallbackHandler {
397
398
            @Override
399
            public void handleCallback(Reference ref) {
400
                ((TestWeakWithFinalize) ref).cleanup.referenceCleared(ref);
401
            }
402
            
403
        }
404
        
405
    }
406
407
    static interface SupportCallback {
408
        void referenceCleared(Reference ref);
409
    }
410
        
411
    private static class LoggerWarningsHandler extends Handler {
412
        
413
        private final Map<String, Level> messages = new LinkedHashMap<String, Level>();
414
415
        public LoggerWarningsHandler() {
416
        }
417
418
        @Override
419
        public void publish(LogRecord record) {
420
            Level f = record.getLevel();
421
            if (f.intValue() >= Level.CONFIG.intValue()) {
422
                messages.put(record.getMessage(), f);
423
            }
424
        }
425
426
        @Override
427
        public void flush() {
428
        }
429
430
        @Override
431
        public void close() throws SecurityException {
432
        }
433
        
434
        public boolean hasMessages() {
435
            return !messages.isEmpty();
436
        }
437
438
        @Override
439
        public String toString() {
440
            return "LoggerWarningsHandler: "+messages.toString();
441
        }
442
        
443
    }
444
    
445
}
(-)a/openide.util/apichanges.xml (+20 lines)
Lines 50-55 Link Here
50
        <apidef name="xml_base">XML API</apidef>
50
        <apidef name="xml_base">XML API</apidef>
51
    </apidefs>
51
    </apidefs>
52
    <changes>
52
    <changes>
53
    <change id="callback-references">
54
        <api name="util_base"/>
55
        <summary>CallbackReferences class provides references that are able to
56
            call a defined callback, after the referent object becomes unreachable.</summary>
57
        <version major="9" minor="8"/>
58
        <date year="2015" month="12" day="15"/>
59
        <author login="mentlicher"/>
60
        <compatibility addition="yes" binary="compatible" source="compatible"/>
61
        <description>
62
            <p>
63
                <code>CallbackReferences</code> class provides a set of reference
64
                implementations that are able to process the given callback runnable,
65
                after their referent object becomes unreachable. The callback
66
                processing is performed on a dedicated daemon thread,
67
                which is shut down when there are no more callbacks to process.
68
            </p>
69
        </description>
70
        <class package="org.openide.util" name="CallbackReferences"/>
71
        <issue number="257013"/>
72
    </change>
53
    <change id="weak-listen-on-specific-property">
73
    <change id="weak-listen-on-specific-property">
54
        <api name="util_base"/>
74
        <api name="util_base"/>
55
        <summary>Weak property and vetoable listeners for a specific property name.</summary>
75
        <summary>Weak property and vetoable listeners for a specific property name.</summary>
(-)a/openide.util/manifest.mf (-1 / +1 lines)
Lines 1-5 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.openide.util
2
OpenIDE-Module: org.openide.util
3
OpenIDE-Module-Localizing-Bundle: org/openide/util/base/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/openide/util/base/Bundle.properties
4
OpenIDE-Module-Specification-Version: 9.7
4
OpenIDE-Module-Specification-Version: 9.8
5
5
(-)a/openide.util/src/org/openide/util/BaseUtilities.java (+2 lines)
Lines 224-229 Link Here
224
     * Be sure to call this method anew for each reference.
224
     * Be sure to call this method anew for each reference.
225
     * Do not attempt to cache the return value.
225
     * Do not attempt to cache the return value.
226
     * @since 3.11
226
     * @since 3.11
227
     * @Deprecated This API requires an indefinitely running background thread.
228
     *             Use {@link CallbackReferences} instead.
227
     */
229
     */
228
    public static ReferenceQueue<Object> activeReferenceQueue() {
230
    public static ReferenceQueue<Object> activeReferenceQueue() {
229
        return ActiveQueue.queue();
231
        return ActiveQueue.queue();
(-)a/openide.util/src/org/openide/util/CallbackReferences.java (+301 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.openide.util;
43
44
import java.lang.ref.PhantomReference;
45
import java.lang.ref.Reference;
46
import java.lang.ref.SoftReference;
47
import java.lang.ref.WeakReference;
48
import java.util.concurrent.atomic.AtomicBoolean;
49
import org.openide.util.lookup.implspi.CallbackReferencesSupport;
50
import org.openide.util.lookup.implspi.CallbackReferencesSupport.CallbackHandler;
51
52
/**
53
 * This class provides references, that are able to call a defined callback,
54
 * after the referent object becomes unreachable. It is useful when there is
55
 * a need to run a callback code after the referent is collected.
56
 * <p>
57
 * Usually, in order to implement such logic, one needs to either create
58
 * a dedicated thread that blocks on the queue and is <code>Object.notify</code>-ed,
59
 * which is the right approach but consumes valuable system resources (threads),
60
 * or one can periodically check the content of the queue by
61
 * <code>RequestProcessor.Task.schedule</code> which is completely wrong,
62
 * because it wakes up the system every (say) 15 seconds.
63
 * <p>
64
 * In order to provide an efficient support for this problem, these references
65
 * has been provided. They share one background thread to process the callbacks,
66
 * which dies when there are no more callbacks to process.<br>
67
 * Be sure not to block in such callbacks for a long time as this prevents other
68
 * waiting references from processing.
69
 * 
70
 * @author Martin Entlicher
71
 * @since 9.8
72
 */
73
public final class CallbackReferences {
74
    
75
    private CallbackReferences() {}
76
    
77
    private static <T> T referentCheck(T referent) {
78
        if (referent == null) {
79
            throw new NullPointerException("The referent must not be null.");   // NOI18N
80
        }
81
        return referent;
82
    }
83
    
84
    private static Callback checkCallback(Callback callback) {
85
        if (callback == null) {
86
            throw new NullPointerException("The callback must not be null.");   // NOI18N
87
        }
88
        return callback;
89
    }
90
    
91
    /**
92
     * An implementation of {@link PhantomReference} with a callback functionality.
93
     * After the referent becomes unreachable, the provided Runnable callback is executed.
94
     */
95
    public static class Phantom<T> extends PhantomReference<T> {
96
        
97
        private static final AtomicBoolean REGISTERED = new AtomicBoolean(false);
98
        
99
        private final Callback callback;
100
        
101
        /**
102
         * Create a new phantom reference with a callback. Both referent and the
103
         * callback should not be <code>null</code>.
104
         * @param referent the object the new phantom reference will refer to
105
         * @param callback the callback executed after the referent becomes unreachable.
106
         */
107
        public Phantom(T referent, Callback callback) {
108
            this(referentCheck(referent), checkCallback(callback), CallbackReferencesSupport.getDefault());
109
        }
110
        
111
        private Phantom(T referent, Callback callback, CallbackReferencesSupport support) {
112
            super(referent, support.getReferenceQueue());
113
            this.callback = callback;
114
            support.registerReferenceFinalizer(this);
115
            if (!REGISTERED.getAndSet(true)) {
116
                CallbackReferencesSupport.registerCallbackHandler(Phantom.class, new PhantomCallbackHandler());
117
            }
118
        }
119
        
120
        private static final class PhantomCallbackHandler implements CallbackHandler {
121
            
122
            public void handleCallback(Reference ref) {
123
                ((Phantom) ref).callback.referenceCleared(ref);
124
            }
125
        }
126
    }
127
    
128
    /**
129
     * An implementation of {@link SoftReference} with a callback functionality.
130
     * After the referent becomes unreachable, the provided Runnable callback is executed.
131
     */
132
    public static class Soft<T> extends SoftReference<T> {
133
        
134
        private static final AtomicBoolean REGISTERED = new AtomicBoolean(false);
135
        
136
        private final Callback callback;
137
        
138
        /**
139
         * Create a new soft reference with a callback. Both referent and the
140
         * callback should not be <code>null</code>.
141
         * @param referent the object the new soft reference will refer to
142
         * @param callback the callback executed after the referent becomes unreachable.
143
         */
144
        public Soft(T referent, Callback callback) {
145
            this(referentCheck(referent), checkCallback(callback), CallbackReferencesSupport.getDefault());
146
        }
147
        
148
        private Soft(T referent, Callback callback, CallbackReferencesSupport support) {
149
            super(referent, support.getReferenceQueue());
150
            this.callback = callback;
151
            support.registerReferenceFinalizer(this);
152
            if (!REGISTERED.getAndSet(true)) {
153
                CallbackReferencesSupport.registerCallbackHandler(Soft.class, new SoftCallbackHandler());
154
            }
155
        }
156
        
157
        private static final class SoftCallbackHandler implements CallbackHandler {
158
159
            @Override
160
            public void handleCallback(Reference ref) {
161
                ((Soft) ref).callback.referenceCleared(ref);
162
            }
163
            
164
        }
165
        
166
    }
167
    
168
    /**
169
     * An implementation of {@link WeakReference} with a callback functionality.
170
     * After the referent becomes unreachable, the provided Runnable callback is executed.
171
     */
172
    public static class Weak<T> extends WeakReference<T> {
173
        
174
        private static final AtomicBoolean REGISTERED = new AtomicBoolean(false);
175
        
176
        private final Callback callback;
177
        
178
        /**
179
         * Create a new weak reference with a callback. Both referent and the
180
         * callback should not be <code>null</code>.
181
         * @param referent the object the new weak reference will refer to
182
         * @param callback the callback executed after the referent becomes unreachable.
183
         */
184
        public Weak(T referent, Callback callback) {
185
            this(referentCheck(referent), checkCallback(callback), CallbackReferencesSupport.getDefault());
186
        }
187
        
188
        private Weak(T referent, Callback callback, CallbackReferencesSupport support) {
189
            super(referent, support.getReferenceQueue());
190
            this.callback = callback;
191
            support.registerReferenceFinalizer(this);
192
            if (!REGISTERED.getAndSet(true)) {
193
                CallbackReferencesSupport.registerCallbackHandler(Weak.class, new WeakCallbackHandler());
194
            }
195
        }
196
        
197
        private static final class WeakCallbackHandler implements CallbackHandler {
198
199
            @Override
200
            public void handleCallback(Reference ref) {
201
                ((Weak) ref).callback.referenceCleared(ref);
202
            }
203
            
204
        }
205
        
206
    }
207
    
208
    /**
209
     * The callback, which is notified about cleared references.
210
     */
211
    public static interface Callback {
212
        
213
        /**
214
         * Called when a reference is cleared.
215
         * @param reference The cleared reference.
216
         */
217
        void referenceCleared(Reference reference);
218
        
219
    }
220
    
221
    /**
222
     * An abstract {@link PhantomReference}, which acts itself as a callback
223
     * by implementing {@link Runable}.
224
     * After the referent becomes unreachable, it's <code>run()</code> method
225
     * is executed.
226
     */
227
    public static abstract class PhantomCallback<T> extends PhantomReference<T> implements Runnable {
228
        
229
        private static final AtomicBoolean REGISTERED = new AtomicBoolean(false);
230
        
231
        /**
232
         * Create a new phantom callback reference.
233
         * @param referent the object the new phantom reference will refer to
234
         */
235
        public PhantomCallback(T referent) {
236
            super(referentCheck(referent), CallbackReferencesSupport.getDefault().getReferenceQueue());
237
            CallbackReferencesSupport.getDefault().registerReferenceFinalizer(this);
238
            if (!REGISTERED.getAndSet(true)) {
239
                CallbackReferencesSupport.registerCallbackHandler(PhantomCallback.class, new RunnableCallback());
240
            }
241
        }
242
        
243
    }
244
    
245
    /**
246
     * An abstract {@link SoftReference}, which acts itself as a callback
247
     * by implementing {@link Runable}.
248
     * After the referent becomes unreachable, it's <code>run()</code> method
249
     * is executed.
250
     */
251
    public static abstract class SoftCallback<T> extends SoftReference<T> implements Runnable {
252
        
253
        private static final AtomicBoolean REGISTERED = new AtomicBoolean(false);
254
        
255
        /**
256
         * Create a new soft callback reference.
257
         * @param referent the object the new phantom reference will refer to
258
         */
259
        public SoftCallback(T referent) {
260
            super(referentCheck(referent), CallbackReferencesSupport.getDefault().getReferenceQueue());
261
            CallbackReferencesSupport.getDefault().registerReferenceFinalizer(this);
262
            if (!REGISTERED.getAndSet(true)) {
263
                CallbackReferencesSupport.registerCallbackHandler(SoftCallback.class, new RunnableCallback());
264
            }
265
        }
266
        
267
    }
268
    
269
    /**
270
     * An abstract {@link WeakReference}, which acts itself as a callback
271
     * by implementing {@link Runable}.
272
     * After the referent becomes unreachable, it's <code>run()</code> method
273
     * is executed.
274
     */
275
    public static abstract class WeakCallback<T> extends WeakReference<T> implements Runnable {
276
        
277
        private static final AtomicBoolean REGISTERED = new AtomicBoolean(false);
278
        
279
        /**
280
         * Create a new weak callback reference.
281
         * @param referent the object the new phantom reference will refer to
282
         */
283
        public WeakCallback(T referent) {
284
            super(referentCheck(referent), CallbackReferencesSupport.getDefault().getReferenceQueue());
285
            CallbackReferencesSupport.getDefault().registerReferenceFinalizer(this);
286
            if (!REGISTERED.getAndSet(true)) {
287
                CallbackReferencesSupport.registerCallbackHandler(WeakCallback.class, new RunnableCallback());
288
            }
289
        }
290
        
291
    }
292
    
293
    private static final class RunnableCallback implements CallbackHandler {
294
        
295
        @Override
296
        public void handleCallback(Reference ref) {
297
            ((Runnable) ref).run();
298
        }
299
    }
300
    
301
}
(-)a/openide.util/src/org/openide/util/base/doc-files/api.html (-1 / +6 lines)
Lines 73-83 Link Here
73
    <a href="../../BaseUtilities.html"><code>BaseUtilities</code></a> which contain
73
    <a href="../../BaseUtilities.html"><code>BaseUtilities</code></a> which contain
74
    a lot of methods of possible interest. For example
74
    a lot of methods of possible interest. For example
75
    <a href="../../BaseUtilities.html#topologicalSort(java.util.Collection,%20java.util.Map)"><code>topologicalSort</code></a>,
75
    <a href="../../BaseUtilities.html#topologicalSort(java.util.Collection,%20java.util.Map)"><code>topologicalSort</code></a>,
76
    <a href="../../BaseUtilities.html#activeReferenceQueue()"><code>activeReferenceQueue</code></a>,
77
    <a href="../../BaseUtilities.html#translate(java.lang.String)"><code>translate</code></a>.
76
    <a href="../../BaseUtilities.html#translate(java.lang.String)"><code>translate</code></a>.
78
    </li>
77
    </li>
79
78
80
    <li>
79
    <li>
80
    <a href="../CallbackReferences.html"><code>CallbackReferences</code></a> which contain
81
    implementation of references, that are able to call a defined callback,
82
    after the referent object becomes unreachable.
83
    </li>
84
85
    <li>
81
    <a href="../../Enumerations.html"><code>Enumerations</code></a> provide
86
    <a href="../../Enumerations.html"><code>Enumerations</code></a> provide
82
    enhacened support for manipulation with 
87
    enhacened support for manipulation with 
83
    <a href="@JDK@/java/util/Enumeration.html">Enumeration</a>s and especially
88
    <a href="@JDK@/java/util/Enumeration.html">Enumeration</a>s and especially
(-)a/openide.util/src/org/openide/util/doc-files/api.html (-1 / +6 lines)
Lines 73-83 Link Here
73
    <a href="../BaseUtilities.html"><code>BaseUtilities</code></a> which contain
73
    <a href="../BaseUtilities.html"><code>BaseUtilities</code></a> which contain
74
    a lot of methods of possible interest. For example
74
    a lot of methods of possible interest. For example
75
    <a href="../BaseUtilities.html#topologicalSort-java.util.Collection-java.util.Map-"><code>topologicalSort</code></a>,
75
    <a href="../BaseUtilities.html#topologicalSort-java.util.Collection-java.util.Map-"><code>topologicalSort</code></a>,
76
    <a href="../BaseUtilities.html#activeReferenceQueue--"><code>activeReferenceQueue</code></a>,
77
    <a href="../BaseUtilities.html#translate-java.lang.String-"><code>translate</code></a>.
76
    <a href="../BaseUtilities.html#translate-java.lang.String-"><code>translate</code></a>.
78
    </li>
77
    </li>
79
78
80
    <li>
79
    <li>
80
    <a href="../CallbackReferences.html"><code>CallbackReferences</code></a> which contain
81
    implementation of references, that are able to call a defined callback,
82
    after the referent object becomes unreachable.
83
    </li>
84
85
    <li>
81
    <a href="../Enumerations.html"><code>Enumerations</code></a> provide
86
    <a href="../Enumerations.html"><code>Enumerations</code></a> provide
82
    enhacened support for manipulation with 
87
    enhacened support for manipulation with 
83
    <a href="@JDK@/java/util/Enumeration.html">Enumeration</a>s and especially
88
    <a href="@JDK@/java/util/Enumeration.html">Enumeration</a>s and especially
(-)a/openide.util/test/unit/src/org/openide/util/CallbackReferencesTest.java (+236 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.openide.util;
43
44
import java.lang.ref.Reference;
45
import java.lang.reflect.Method;
46
import static junit.framework.TestCase.assertTrue;
47
import org.netbeans.junit.NbTestCase;
48
import static org.netbeans.junit.NbTestCase.assertGC;
49
import org.openide.util.lookup.implspi.CallbackReferencesSupport;
50
51
/**
52
 *
53
 * @author Martin Entlicher
54
 */
55
public class CallbackReferencesTest extends NbTestCase {
56
    
57
    public CallbackReferencesTest(String testName) {
58
        super(testName);
59
    }
60
    
61
    private boolean isDaemonActive() throws Exception {
62
        // CallbackReferencesSupport.Daemon.isActive()
63
        Class daemonClass = Class.forName("org.openide.util.lookup.implspi.CallbackReferencesSupportFinishable");
64
        Method isActiveMethod = daemonClass.getDeclaredMethod("isDaemonRunning");
65
        isActiveMethod.setAccessible(true);
66
        return (Boolean) isActiveMethod.invoke(null);
67
    }
68
69
    @Override
70
    protected void tearDown() throws Exception {
71
        // Test that the Daemon thread finishes eventually
72
        while (isDaemonActive()) {
73
            System.gc();
74
            Thread.sleep(10);
75
        }
76
    }
77
    
78
    public void testRunnableReferenceIsExecuted () throws Exception {
79
        Object obj = new Object ();
80
        RunnableRef ref = new RunnableRef (obj);
81
        synchronized (ref) {
82
            obj = null;
83
            assertGC ("Should be GCed quickly", ref);
84
            ref.wait ();
85
            assertTrue ("Run method has been executed", ref.executed);
86
        }
87
    }
88
    
89
    public void testRunnablesAreProcessedOneByOne () throws Exception {
90
        Object obj = new Object ();
91
        RunnableRef ref = new RunnableRef (obj);
92
        ref.wait = true;
93
        
94
        
95
        synchronized (ref) {
96
            obj = null;
97
            assertGC ("Is garbage collected", ref);
98
            ref.wait ();
99
            assertTrue ("Still not executed, it is blocked", !ref.executed);
100
        }    
101
102
        RunnableRef after = new RunnableRef (new Object ());
103
        synchronized (after) {
104
            assertGC ("Is garbage collected", after);
105
            after.wait (100); // will fail
106
            assertTrue ("Even if GCed, still not processed", !after.executed);
107
        }
108
109
        synchronized (after) {
110
            synchronized (ref) {
111
                ref.notify ();
112
                ref.wait ();
113
                assertTrue ("Processed", ref.executed);
114
            }
115
            after.wait ();
116
            assertTrue ("Processed too", after.executed);
117
        }
118
    }
119
    
120
    public void testManyReferencesProcessed() throws InterruptedException {
121
        int n = 100;
122
        Object[] objects = new Object[n];
123
        ExpensiveRef[] refs = new ExpensiveRef[n];
124
        for (int i = 0; i < n; i++) {
125
            objects[i] = new Object();
126
            refs[i] = new ExpensiveRef(objects[i], Integer.toString(i));
127
        }
128
        objects = null;
129
        for (int i = 0; i < n; i++) {
130
            assertGC("is GC'ed", refs[i]);
131
        }
132
        for (int i = 0; i < n; i++) {
133
            synchronized (refs[i]) {
134
                while (!refs[i].executed) {
135
                    refs[i].wait();
136
                }
137
            }
138
        }
139
    }
140
    
141
    public void testNullNotAccepted() {
142
        NullPointerException npe = null;
143
        try {
144
            new CallbackReferences.Phantom<Object>(null, new CallbackReferences.Callback() { public void referenceCleared(Reference reference) {} });
145
        } catch (NullPointerException ex) {
146
            npe = ex;
147
        }
148
        assertNotNull("NullPointerException not thrown!", npe);
149
        npe = null;
150
        try {
151
            new CallbackReferences.Phantom<Object>(new Object(), null);
152
        } catch (NullPointerException ex) {
153
            npe = ex;
154
        }
155
        assertNotNull("NullPointerException not thrown!", npe);
156
        npe = null;
157
        try {
158
            new CallbackReferences.Soft<Object>(null, new CallbackReferences.Callback() { public void referenceCleared(Reference reference) {} });
159
        } catch (NullPointerException ex) {
160
            npe = ex;
161
        }
162
        assertNotNull("NullPointerException not thrown!", npe);
163
        npe = null;
164
        try {
165
            new CallbackReferences.Soft<Object>(new Object(), null);
166
        } catch (NullPointerException ex) {
167
            npe = ex;
168
        }
169
        assertNotNull("NullPointerException not thrown!", npe);
170
        npe = null;
171
        try {
172
            new CallbackReferences.Weak<Object>(null, new CallbackReferences.Callback() { public void referenceCleared(Reference reference) {} });
173
        } catch (NullPointerException ex) {
174
            npe = ex;
175
        }
176
        assertNotNull("NullPointerException not thrown!", npe);
177
        npe = null;
178
        try {
179
            new CallbackReferences.Weak<Object>(new Object(), null);
180
        } catch (NullPointerException ex) {
181
            npe = ex;
182
        }
183
        assertNotNull("NullPointerException not thrown!", npe);
184
    }
185
    
186
    private static class RunnableRef extends CallbackReferences.WeakCallback<Object>
187
    implements Runnable {
188
        public boolean wait;
189
        public boolean entered;
190
        public boolean executed;
191
        
192
        public RunnableRef (Object o) {
193
            super(o);
194
        }
195
        
196
        @Override
197
        public synchronized void run () {
198
            entered = true;
199
            if (wait) {
200
                // notify we are here
201
                notify ();
202
                try {
203
                    wait ();
204
                } catch (InterruptedException ex) {
205
                }
206
            }
207
            executed = true;
208
            
209
            notifyAll ();
210
        }
211
    }
212
    
213
    private static class ExpensiveRef extends CallbackReferences.WeakCallback<Object>
214
    implements Runnable {
215
        public boolean executed;
216
        private final String name;
217
        
218
        public ExpensiveRef (Object o, String name) {
219
            super(o);
220
            this.name = name;
221
        }
222
        
223
        @Override
224
        public synchronized void run () {
225
            executed = true;
226
            try {
227
                Thread.sleep(10);
228
                System.gc();
229
                Thread.sleep(10);
230
            } catch (InterruptedException iex) {}
231
            notifyAll ();
232
            //System.err.println(name+" executed.");
233
        }
234
    }
235
    
236
}

Return to bug 257013