You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by hl...@apache.org on 2003/09/29 22:56:19 UTC

cvs commit: jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/commons/hivemind/service/impl ThreadEventNotifierImpl.java

hlship      2003/09/29 13:56:19

  Modified:    hivemind/framework/src/java/org/apache/commons/hivemind/service/impl
                        ThreadEventNotifierImpl.java
  Added:       hivemind/framework/src/test/hivemind/test/util
                        TestEventListenerList.java
               hivemind/framework/src/java/org/apache/commons/hivemind/util
                        EventListenerList.java
  Log:
  Create utility class EventListenerList, used for managing listeners lists (for services like ThreadEventNotifier).
  
  Revision  Changes    Path
  1.1                  jakarta-commons-sandbox/hivemind/framework/src/test/hivemind/test/util/TestEventListenerList.java
  
  Index: TestEventListenerList.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2003 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package hivemind.test.util;
  
  import hivemind.test.FrameworkTestCase;
  
  import java.util.Iterator;
  
  import org.apache.commons.hivemind.util.EventListenerList;
  
  /**
   * Tests for {@link org.apache.commons.hivemind.util.EventListenerList}.
   *
   * @author Howard Lewis Ship
   * @version $Id: TestEventListenerList.java,v 1.1 2003/09/29 20:56:18 hlship Exp $
   */
  public class TestEventListenerList extends FrameworkTestCase
  {
      private static class Trigger
      {
          private boolean _trigger;
  
          public boolean isTrigger()
          {
              return _trigger;
          }
  
          public void setTrigger(boolean b)
          {
              _trigger = b;
          }
  
      }
  
      public TestEventListenerList(String name)
      {
          super(name);
      }
  
      private Trigger[] buildTriggers(int count)
      {
          Trigger[] result = new Trigger[count];
  
          for (int i = 0; i < count; i++)
          {
              result[i] = new Trigger();
          }
  
          return result;
      }
  
      private void addAll(EventListenerList l, Trigger[] t)
      {
          for (int i = 0; i < t.length; i++)
              l.addListener(t[i]);
      }
  
      private void checkAllTrue(Trigger[] t)
      {
          for (int i = 0; i < t.length; i++)
              assertEquals(true, t[i].isTrigger());
      }
  
      public void testBasic()
      {
          EventListenerList l = new EventListenerList();
  
          Trigger[] ta = buildTriggers(20);
  
          addAll(l, ta);
  
          Iterator i = l.getListeners();
  
          while (i.hasNext())
          {
              Trigger t = (Trigger) i.next();
  
              t.setTrigger(true);
          }
  
      }
  
      public void testEmptyList()
      {
          EventListenerList l = new EventListenerList();
  
          Iterator i = l.getListeners();
  
          assertEquals(false, i.hasNext());
      }
  
      public void testLateAdd()
      {
          Trigger[] ta = buildTriggers(20);
          EventListenerList l = new EventListenerList();
  
          addAll(l, ta);
  
          Iterator i = l.getListeners();
  
          for (int j = 0; j < 5; j++)
          {
              Trigger t = (Trigger) i.next();
              t.setTrigger(true);
          }
  
          Trigger tnew = new Trigger();
          l.addListener(tnew);
  
          while (i.hasNext())
          {
              Trigger t = (Trigger) i.next();
              t.setTrigger(true);
          }
  
          assertEquals(false, tnew.isTrigger());
  
          checkAllTrue(ta);
      }
  
      public void testLateRemove()
      {
          Trigger[] ta = buildTriggers(20);
          EventListenerList l = new EventListenerList();
  
          addAll(l, ta);
  
          Iterator i = l.getListeners();
  
          for (int j = 0; j < 5; j++)
          {
              Trigger t = (Trigger) i.next();
              t.setTrigger(true);
          }
  
          Trigger tremoved = ta[15];
          l.removeListener(tremoved);
  
          while (i.hasNext())
          {
              Trigger t = (Trigger) i.next();
              t.setTrigger(true);
          }
  
          checkAllTrue(ta);
      }
  
      public void testRemoveMissing()
      {
          Trigger[] ta = buildTriggers(20);
          EventListenerList l = new EventListenerList();
  
          addAll(l, ta);
  
          Trigger tremove = new Trigger();
  
          l.removeListener(tremove);
  
          Iterator i = l.getListeners();
          while (i.hasNext())
          {
              Trigger t = (Trigger) i.next();
              t.setTrigger(true);
          }
  
          checkAllTrue(ta);
      }
  
      public void testIteratorRemoveFailure()
      {
          Trigger[] ta = buildTriggers(20);
          EventListenerList l = new EventListenerList();
  
          addAll(l, ta);
  
          Iterator i = l.getListeners();
  
          for (int j = 0; j < 5; j++)
              i.next();
  
          try
          {
              i.remove();
              unreachable();
          }
          catch (UnsupportedOperationException ex)
          {
          }
      }
  }
  
  
  
  1.1                  jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/commons/hivemind/util/EventListenerList.java
  
  Index: EventListenerList.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2003 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.hivemind.util;
  
  import java.util.Iterator;
  
  /**
   * Convienience class for tracking a list of event listeners. Works efficiently
   * (using a copy-on-write approach) to iterating through the listeners in
   * the list even when the list of listeners may be modified.
   * 
   * <p>
   * EventListenerList is <em>not</em> thread-safe.
   *
   * @author Howard Lewis Ship
   * @version $Id: EventListenerList.java,v 1.1 2003/09/29 20:56:19 hlship Exp $
   */
  public class EventListenerList
  {
      private static final int START_SIZE = 5;
  
      private Object[] _listeners;
      private int _count;
      private int _iteratorCount;
      private int _uid;
  
      private class ListenerIterator implements Iterator
      {
      	private Object[] _localListeners;
      	private int _localCount;
      	private int _localUid;
      	private int _pos;
      	
      	private ListenerIterator()
      	{
      		_localListeners = _listeners;
      		_localCount = _count;
      		_localUid = _uid;
      	}
      	
          public boolean hasNext()
          {
  			if (_pos >= _localCount)
  			{
  				// If _listeners has not been recopied during the lifespan
  				// of this iterator, then knock the count down by one.
  				
  				if (_uid == _localUid)
  					_iteratorCount--;
  	
  				_localListeners = null;
  				_localCount = 0;
  				_localUid = -1;
  				_pos = 0;
  				
  				return false;			
  			}
  			
  			return true;
          }
  
          public Object next()
          {
  			return _localListeners[_pos++];
          }
  
          public void remove()
          {
              throw new UnsupportedOperationException();
          }
  
      }
      
      /**
       * Returns an Iterator used to find all the listeners previously added.
       * The order in which listeners are returned is not guaranteed.
       * Currently, you may not invoke <code>remove()</code> on the Iterator.
       */
  	public Iterator getListeners()
  	{
  		_iteratorCount++;
  		
  		return new ListenerIterator();
  	}
  
  	/**
  	 * Adds a new listener to the list of listeners. The same instance
  	 * will may be added multiple times.
  	 */
      public void addListener(Object listener)
      {
          copyOnWrite(_count + 1);
  
          _listeners[_count] = listener;
  
          _count++;
      }
  
  	/**
  	 * Removes a listener from the list.  Does nothing if the listener
  	 * is not already in the list. Comparison is based on identity, not equality.
  	 * If the listener is in the list multiple times, only a single
  	 * instance is removed.
  	 */
      public void removeListener(Object listener)
      {
          for (int i = 0; i < _count; i++)
          {
              if (_listeners[i] == listener)
              {
  				removeListener(i);
                  return;
              }
          }
      }
      
      private void removeListener(int index)
      {
  		copyOnWrite(_count);
  
  	   	// Move the last listener in the list into the index to be removed.
      	
      	_listeners[index] = _listeners[_count - 1];
      	
      	// Null out the old position.
      	
      	_listeners[_count - 1] = null;
      	
      	_count--;
      }
  
  	/**
  	 * Copies the array before an update operation if necessary (because there
  	 * is a known iterator for the current array, or because the 
  	 * array is not large enough).
  	 */
      private void copyOnWrite(int requiredSize)
      {
          int size = _listeners == null ? 0 : _listeners.length;
  
          if (_iteratorCount > 0 || size < requiredSize)
          {
              int newSize = Math.max(requiredSize, START_SIZE);
  
              Object[] newListeners = new Object[newSize];
  
              if (_count > 0)
                  System.arraycopy(_listeners, 0, newListeners, 0, _count);
  
              _listeners = newListeners;
  
              // No iterators on the *new* array
              _iteratorCount = 0;
              _uid++;
          }
      }
  
  }
  
  
  
  1.2       +14 -17    jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/commons/hivemind/service/impl/ThreadEventNotifierImpl.java
  
  Index: ThreadEventNotifierImpl.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/commons/hivemind/service/impl/ThreadEventNotifierImpl.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ThreadEventNotifierImpl.java	16 Sep 2003 18:51:10 -0000	1.1
  +++ ThreadEventNotifierImpl.java	29 Sep 2003 20:56:19 -0000	1.2
  @@ -57,12 +57,11 @@
   
   package org.apache.commons.hivemind.service.impl;
   
  -import java.util.List;
  -import java.util.ListIterator;
  +import java.util.Iterator;
   
  -import org.apache.commons.collections.CursorableLinkedList;
   import org.apache.commons.hivemind.service.ThreadCleanupListener;
   import org.apache.commons.hivemind.service.ThreadEventNotifier;
  +import org.apache.commons.hivemind.util.EventListenerList;
   
   /**
    * Implementation of {@link org.apache.commons.hivemind.service.ThreadEventNotifier},
  @@ -77,26 +76,23 @@
   
       public void addThreadCleanupListener(ThreadCleanupListener listener)
       {
  -        List list = (List) _storage.get();
  +        EventListenerList list = (EventListenerList) _storage.get();
   
           if (list == null)
           {
  -            // We use this implementation since it is less testy
  -            // about concurrent modifications.
  -
  -            list = new CursorableLinkedList();
  +            list = new EventListenerList();
               _storage.set(list);
           }
   
  -        list.add(listener);
  +        list.addListener(listener);
       }
   
       public void removeThreadCleanupListener(ThreadCleanupListener listener)
       {
  -        List list = (List) _storage.get();
  +		EventListenerList list = (EventListenerList) _storage.get();
   
           if (list != null)
  -            list.remove(listener);
  +            list.removeListener(listener);
       }
   
       public void fireThreadCleanup()
  @@ -105,20 +101,21 @@
           // are free to unregister as listeners from threadDidCleanup() and
           // we need to avoid concurrent modification errors.
   
  -        CursorableLinkedList list = (CursorableLinkedList) _storage.get();
  +		EventListenerList list = (EventListenerList) _storage.get();
   
           if (list == null)
               return;
   
  -        // cursor() returns a ListIterator that isn't bothered by
  -        // modifications to the list.
  -
  -        ListIterator i = list.cursor();
  +        Iterator i = list.getListeners();
   
           while (i.hasNext())
           {
               ThreadCleanupListener listener = (ThreadCleanupListener) i.next();
   
  +			// Each listener may decide to remove itself; that's OK,
  +			// EventListenerList handles that kind of concurrent modification
  +			// well.
  +			
               listener.threadDidCleanup();
           }
   
  
  
  

Re: cvs commit: jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/commons/hivemind/service/impl ThreadEventNotifierImpl.java

Posted by Christian Essl <ch...@yahoo.de>.
The copy-go works certainly more efficient, but has some threading 
considerations.

I think that a Listener expects rightfully that it won't receive an event 
anymore after the deregister-method returns however this is not garanteed 
with copy-go (a mistake I also made with my event-service).

I guess this problem does not apply to the ThreadListenerService, but 
that's realy a special case. So I would not make the class a general util 
(or at least document on it).

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: cvs commit: jakarta-commons-sandbox/hivemind/framework/src/java/org/apache/commons/hivemind/service/impl ThreadEventNotifierImpl.java

Posted by Christian Essl <ch...@yahoo.de>.
The copy-go works certainly more efficient, but has some threading 
considerations.

I think that a Listener expects rightfully that it won't receive an event 
anymore after the deregister-method returns however this is not garanteed 
with copy-go (a mistake I also made with my event-service).

I guess this problem does not apply to the ThreadListenerService, but 
that's realy a special case. So I would not make the class a general util 
(or at least document on it).