Package simtools.util

Source Code of simtools.util.ListenerManager$ReferenceCountedPointer

/*
* Created on 23 mars 2004
*
* To change the template for this generated file go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
package simtools.util;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;


/**
* This class avoids code duplication in as many place as possible.
* Its goal is to provide a listener lists using reference counted pointers,
* so that multiple listener registration still results in only one notification, and
* there is no memory leaks. See the long comment below.
*
* Warning : use external synchronization on the manager before using the
* size() and get(int) methods, as otherwise the size may vary according
* to the whims of the garbage collector.
* The synchronized block should englobe both the size() and get(int) calls.
*
* @author nicolas brodu
*
* <p>
* Tricky Java...
*
* When an object registered as listener becomes unused, the fact that the listener list holds
* a reference to it prevents it from beeing fred by the garbage collector. This ends up in
* objects not being fred, and growing listener lists of unused objects.
* This is bad, and listener lists are a very common source of memory leak! Beware!
*
* A naive and developper-unfriendly approach would be to tell all registering
* objects to unregister before they are unsued... This is easy to do in C++ where
* there are proper destructors, but in Java the finalize() method is precisely called
* by the garbage collector, which won't call it in the present case since the listener
* list still holds a reference. Back to the beginning. An even-more developer unfriendly
* approach would impose a constraint on all objects using objects registered as listeners
* to do the cleanup, but this is a real mess and does not even work for other reasons (for
* example, think of inner classes registering private members as listeners, or other awful
* cases where the object using another object using another object using... has no way of
* unregistering a listerner without modifying drastically the API just for that).
*
* Weak references were introduced in Java 1.2 to solve this problem. If only weak references
* to an object remains, the garbage collector discards it. The get() method of the weak reference
* holder then returns null. A queue can be used to be notified when this happens, as the
* application does not decides when the object is finalized (this is handled by the garbage
* collector thread).
*
* Thus, weak reference pointers may become invalid any time!
*
* For JSynoptic, we have an additional problem. The same object may be registered as listener
* several times, but should be notified only once. See EndNotificationListener for a concrete
* example of this. Also, a Set is not adapted to hold only one reference of each listener.
* If an object is registered twice by two different paths and unregistered only once, it
* is still expected to be notified by the second registering path. Hence, it must still
* be present in the listener list, which would not be the case using a Set.
*
* The solution is of course reference counting, which is applied on the register/unregister
* operation. This is complementary to the WeakReference mecanism : if an object forgets to
* unregister or doesn't have the occasion to do it before being thrown away, then the weak
* reference mecanism will still discard it even if the refcount field is >1, which is what we
* want. On the other hand, if the refcount field goes down to 0, the object is removed from the
* listener list, but this does not necessarily mean the listener is unused. It may just have
* unregistered and live its life as usual. As in this case the listener list does not hold any
* reference to the object, it cannot be the source for a memory leak.
*
* So, the refcount field is <b>NOT</b> related to the weak reference mecanism. It is necessary for
* another reason entirely, as decribed above.
*
* See the notes below for more fun with the ReferenceCountedPointer class.
* It holds a weak reference to the ListenerManager, so as to avoid the same problem
* as above, but for the managers themselves.
* </p>
*
*/
public class ListenerManager {

    /**
     * This inner class is a Weak Reference using refcounting, as described in the note above.
     * It is a static inner class because the ListenerManager reference would otherwise
     * prevent the garbage-collector to destroy the ListenerManagers themselves.
     *
     * Unfortunately, a reference to the listener manager is still required, so
     * as to make only one ReferenceQueue helper thread, and not one thread per
     * ListenerManager. So, use a weak reference to the lisener manager too!
     */
    public class ReferenceCountedPointer extends WeakReference {
        public int refcount = 1; // has ref count of one at creation
        public WeakReference manager;
       
        public ReferenceCountedPointer(ListenerManager mgr, Object o) {
            super(o);
            manager = new WeakReference(mgr);
        }
    }

    /**
     * This is a common queue, not attached to any listener manager, which handles
     * notifications from the garbage collector when a ReferenceCountedPointer has
     * become invalid
     */
    static protected ReferenceQueue queue;
   
    /**
     * Start a thread to manage the queue
     * The thread is passive and wakes up when a weak reference is destroyed
     * Put this in a static area, so that it is always available, and also because
     * it won't hold a reference to Listener managers so they can be fred by the garbage
     * collector
     */
    static {
        queue = new ReferenceQueue();
   
        Thread queueThread = new Thread("ListenerManagerThread") {
            public void run() {
                // Run till the JVM stops. See setDaemon below.
                while (true) {
                    try {
                        // remove is a blocking operation, it doesn't hog the CPU.
                        // Only ReferenceCountedPointer objects are added to the queue
                        // => can safely cast
                        ReferenceCountedPointer rcp = (ReferenceCountedPointer)queue.remove();
                        // mgr may itself be null if the manager was disposed
                        ListenerManager mgr = (ListenerManager)rcp.manager.get();
                        if (mgr!=null) mgr.removeReference(rcp);
                        // Tells the garbage collector to dispose of the Reference object
                        rcp = null;
                    }
                    catch (InterruptedException e) { }
                }
            }
        };
        // Allow the application to terminate without waiting for this thread to stop
        // => allow this thread to loop forever!
        queueThread.setDaemon(true);
        // starts the queue in static context before any instance are created
        queueThread.start();
    }
   

    protected ArrayList listeners; // Lazy created
 
    // Internal use only, synchronized
    protected synchronized void removeReference(ReferenceCountedPointer r) {
        if (listeners!=null) listeners.remove(r);
    }
   
    // Listeners related functions. Take care of duplicates

  public synchronized void add(Object l) {
    if (l==null) return;
    if (listeners == null) listeners = new ArrayList();
    for (Iterator it = listeners.iterator(); it.hasNext(); ) {
        ReferenceCountedPointer rcp = (ReferenceCountedPointer)it.next();
        // get should not be null because queue thread takes care of that
        // But thanks to synchronize magic, the queue thread may be blocked waiting
        // for the add function to terminate.
        // In this case, it doesn't matter, since the equals call will fail and
        // another reference will be added to the list
        if (l.equals(rcp.get())) {
            rcp.refcount++; return;
        }
    }
    listeners.add(new ReferenceCountedPointer(this, l));
  }

  public synchronized void remove(Object l) {
    if (l==null) return;
    if (listeners == null) return;
    ReferenceCountedPointer toRemove = null;
    for (Iterator it = listeners.iterator(); it.hasNext(); ) {
        ReferenceCountedPointer rcp = (ReferenceCountedPointer)it.next();
        // See comment for get() being null in add()
        // This doesn't matter either here but for another reason: a null reference
        // means really that the listener has become unused.
        // The refcount doesn't matter in this case (see class notes), and
        // the queue thread will remove the reference in due time.
        // So, toRemove will be false here, but this is OK.
        if (l.equals(rcp.get())) {
            if (--rcp.refcount>0) return;
            toRemove = rcp; break;
        }
    }
    if (toRemove!=null) listeners.remove(toRemove);
  }

    public synchronized void clear() {
        // keep allocation of the internal arraylist's array, don't set it to null
        listeners.clear();
    }

    /**
     * Warning, use external synchronization on the manager before using this.
     * The synchronized block should englobe both the size() and get(int) calls.
     */
    public int size() {
        return (listeners==null) ? 0 : listeners.size();
    }

    /**
     * Warning, use external synchronization on the manager before using this.
     * The synchronized block should englobe both the size() and get(int) calls.
     * Warning2: This method may return null.
     */
    public Object get(int i) {
      ReferenceCountedPointer rcp = (ReferenceCountedPointer)listeners.get(i);
      return rcp.get();
    }

}
TOP

Related Classes of simtools.util.ListenerManager$ReferenceCountedPointer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.