Event listener list implementation

S

Slobodan C

I am looking for java.util.List implementation for event listeners
that merges several concepts:

1) Jakarta Commons Collection library has a
org.apache.commons.collections.FastArrayList which avoid the overhead
of synchronization using copy-on-write mode. The idea is that when
dominant ops are reads (such as event listener lists) then it is
cheaper to copy entire list before changing it to avoid
synchronization on reads.

2) Related to (1), when firing events using an iterator, iterator
object needs to keep traversing the old copy of a list if the list is
changed during event firings. Although FastArrayList claims to have
this behaviour, it does the opposite and throws the exception (see
Appendix A)

3) As has been discussed many times before, listeners must be
referenced using a weak reference. Would be nice if the list
implementation would take care of it internally and caller need not
worry about it.

My question is if such an implementation exists some place.

Sub question: If you think that (1) and (2) are overkill and
synchronization is simpler and as fast as copy-on-write, tell me why.


Appendix A:

From the Commons Collection 2.1 doc,
org.apache.commons.collections.FastArrayList:

public Iterator iterator()

Return an iterator over the elements in this list in proper
sequence.

IMPLEMENTATION NOTE - If the list is operating in fast mode, an
Iterator is returned, and a structural modification to the list is
made, then the Iterator will continue over the previous contents of
the list (at the time that the Iterator was created), rather than
failing due to concurrent modifications.


Test:

FastArrayList l = new FastArrayList();

l.setFast( false );
l.add("a");
l.add("b");
l.add("c");
l.setFast( true );
System.out.println("FAST = "+ l.getFast());

Iterator seq = l.iterator();
System.out.println("BEFORE "+ seq.next());
l.add("Z");
System.out.println("AFTER "+ seq.next());


FAST = true
BEFORE a
java.util.ConcurrentModificationException
at org.apache.commons.collections.FastArrayList$ListIter.checkMod(Unknown
Source)
at org.apache.commons.collections.FastArrayList$ListIter.next(Unknown
Source)
at brisi.main(brisi.java:22)
Exception in thread "main"

According to the doc I should not get the exception since the list is
in fast mode. Yet it is thrown and looking at the source it seems to
be a deliberate check, so no accident. I can only assume that the doc
hasn't been updated.
 
X

xarax

Slobodan C said:
I am looking for java.util.List implementation for event listeners
that merges several concepts:

1) Jakarta Commons Collection library has a
org.apache.commons.collections.FastArrayList which avoid the overhead
of synchronization using copy-on-write mode. The idea is that when
dominant ops are reads (such as event listener lists) then it is
cheaper to copy entire list before changing it to avoid
synchronization on reads.

Don't know about this one...
2) Related to (1), when firing events using an iterator, iterator
object needs to keep traversing the old copy of a list if the list is
changed during event firings. Although FastArrayList claims to have
this behaviour, it does the opposite and throws the exception (see
Appendix A)

When I am ready to fire an event, I make an
array of the current listener list, then
walk the array. The listener list can change
while firing the event, but only the listeners
that were registered at the time of the event
will be notified. Creating the array is an
atomic event (synchronized), so it is somewhat
fast. Walking the array is not synchronized,
because no other thread has a reference to
the array.

3) As has been discussed many times before, listeners must be
referenced using a weak reference. Would be nice if the list
implementation would take care of it internally and caller need not
worry about it.

This is not too hard to create an abstract class
implementation of the bare bones, then extend it
with a nested class for handling your specific
event type. Hide the list as a private field and
use instance methods to manage adding, removing,
event firing, etc. Use synchronization as necessary
in those instance methods. ArrayList is fast, and
you can wrap the listener elements with a WeakReference.
My question is if such an implementation exists some place.

Sub question: If you think that (1) and (2) are overkill and
synchronization is simpler and as fast as copy-on-write, tell me why.
/snip/

What is the minimum functionality that you need? Specify
each requirement and be certain that it is a *requirement*,
rather than a "nice to have feature".
 
S

Slobodan C

xarax said:
When I am ready to fire an event, I make an
array of the current listener list, then
walk the array. The listener list can change
while firing the event, but only the listeners
that were registered at the time of the event
will be notified. Creating the array is an
atomic event (synchronized), so it is somewhat
fast. Walking the array is not synchronized,
because no other thread has a reference to
the array.
.....

Of course, that it is the traditional solution. However, in practice
original array list 99% of the times will not be modified while
iterating over its elements. Thus we end up creating a copy for each
even firing even though it wasn't necessary.

(actually 99% above depends on a particular context ...)

If a system fires a lot of events it adds up to a lot of garbage being
created at a fast clip!

So I'd like an implementation that starts out with an optimistic
assumption that copy is not needed. Then, while iterating over the
original array if a modification call is made then a copy is made.
Something like:

<Event Firing Thread> <Some other Thread>

Iterator i(a) = listeners(a).iterator();
fireEvent( i(a).next() );
....
fireEvent( i(a).next() ); listeners(a).add( this );
.... {copy} listeners(b)
fireEvent( i(a).next() );
-->>>> <Handler>
do stuff
listeners(b).remove( this
);
{copy} listeners(b)
....
fireEvent( i(a).next() ); listeners(a).add( this );
....

This is sort of pseudo-code. The (x) implies a version of a listener
list. So you can see that regardless of changes on the right the
iterator keeps traversing original list (view). Regardless of changes
being made in another thread or one of the handlers, iterator is fine.
{copy} is meant to show that only at the last moment new copy is made.
Thus ideal case would be:

<Event Firing Thread>

Iterator i(a) = listeners(a).iterator();
fireEvent( i(a).next() );
....
fireEvent( i(a).next() );
<done>

So no copying since there is no need.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top