ThreadProxyEventList

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

ThreadProxyEventList

Merki Fabian (KETO 41)
hi guys

it took me a while to rework the test class - but today i had time.
i'm able to proove with the following test class that glazedlists "sometimes" fails with a following exception:

java.lang.IndexOutOfBoundsException: Cannot get at 4 on list of size 0
        at ca.odell.glazedlists.TransformedList.get(TransformedList.java:115)
        at ca.odell.glazedlists.impl.gui.ThreadProxyEventList$UpdateRunner.listChanged(ThreadProxyEventList.java:191)
        at ca.odell.glazedlists.event.ListEventPublisher.fireEvent(ListEventPublisher.java:150)
        at ca.odell.glazedlists.event.ListEventAssembler.fireEvent(ListEventAssembler.java:272)
        at ca.odell.glazedlists.event.ListEventAssembler.commitEvent(ListEventAssembler.java:242)
        at ca.odell.glazedlists.impl.gui.ThreadProxyEventList$UpdateRunner.run(ThreadProxyEventList.java:162)
        at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:178)

the reason is that the ThreadProxyEventList looks for an already deleted element in the underlying list. in my opinion the lookup should have been done before in the background thread rather than the awt-thread.

if it doesn't fail at the first try, run it again and/or increase the NUMBER_OF_PERSONS.

thanks for any help

fabian

------------------------

import ca.odell.glazedlists.*;
import ca.odell.glazedlists.impl.beans.*;
import ca.odell.glazedlists.swing.*;

import javax.swing.*;
import java.util.*;

public class TableTest {
        private static final int NUMBER_OF_PERSONS = 100;

        public static void main(String[] args) {
                JTable table = new JTable();

                JFrame f = new JFrame();
                f.setContentPane(new JScrollPane(table));
                f.setSize(600, 600);
                f.show();
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                EventList base = new BasicEventList();

                SortedList sortedList = new SortedList(base,
                                new Comparator() {
                                        public int compare(Object o1, Object o2) {
                                                return ((Person) o1).sortB - ((Person) o2).sortB;
                                        }
                                });

                EventTableModel etm = new EventTableModel(sortedList, new BeanTableFormat(Person.class, new String[]{"id", "name"}, new String[]{"ID", "Name"}, new boolean[]{false, false}));

                table.setModel(etm);

                // happens with and without the next line  - to me looks like happens more often with but than "always" on row 0
                new TableComparatorChooser(table, sortedList, false).chooseComparator(1, 0, true);

                for (int tests = 0; tests < 20; tests++) { // run a few tests
                        StringBuffer log = new StringBuffer();

                        for (int id = 0; id < NUMBER_OF_PERSONS; id++) { // add 100 persons
                                base.getReadWriteLock().writeLock().lock();
                                base.add(new Person("Name " + id, id, id, id));
                                base.getReadWriteLock().writeLock().unlock();
                        }

                        for (int i = 0; i < NUMBER_OF_PERSONS; i++) {
                                base.getReadWriteLock().writeLock().lock();
                                int id = (int) (Math.random() * base.size()); // randomly add or reset persons in the list

                                Person p = new Person("Name " + id, id, -id, id);
                                if (base.indexOf(p) != -1) {
                                        base.set(base.indexOf(p), p);
                                        log.append("S").append(id).append(" ");
                                } else {
                                        base.add(p);
                                        log.append("A").append(id).append(" ");
                                }
                                base.getReadWriteLock().writeLock().unlock();

                                base.getReadWriteLock().writeLock().lock();
                                for (int y = 0; y < 5 && base.size() != 0; y++) { // remove randomly max. 5 persons of the list
                                        id = (int) (Math.random() * base.size());
                                        base.remove(id);
                                        log.append("R").append(id).append(" ");
                                }
                                base.getReadWriteLock().writeLock().unlock();
                        }

                        System.out.println(log);

                        mySleep(100);
                }

                System.exit(0);
        }

        private static void mySleep(long millis) {
                try {
                        Thread.sleep(millis);
                } catch (InterruptedException e) {
                }
        }


        public static class Person {
                private String name;
                private int id;

                private int sortA;
                private int sortB;

                public Person(String name, int id, int sortA, int sortB) {
                        this.name = name;
                        this.id = id;
                        this.sortA = sortA;
                        this.sortB = sortB;
                }

                public String getName() {
                        return name;
                }

                public void setName(String name) {
                        this.name = name;
                }

                public int getId() {
                        return id;
                }

                public void setId(int id) {
                        this.id = id;
                }

                public boolean equals(Object o) {
                        if (this == o) return true;
                        if (!(o instanceof Person)) return false;

                        final Person person = (Person) o;

                        if (id != person.id) return false;

                        return true;
                }

                public int hashCode() {
                        return id;
                }
        }

}

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: ThreadProxyEventList

Jesse Wilson-2
Hey Fabian --

Thanks for your great test application, I was able to
quickly find & diagnose the problem. I made the following
change to ThreadProxyEventList to get to the root of the problem,

        /**
         * Update local state as a consequence of the change event.
         */
        public void listChanged(ListEvent listChanges) {
            List before = new ArrayList(localCache);
            try {
                while(listChanges.next()) {
                    int sourceIndex = listChanges.getIndex();
                    int changeType = listChanges.getType();
   
                    if(changeType == ListEvent.DELETE) {
                        localCache.remove(sourceIndex);
                    } else if(changeType == ListEvent.INSERT) {
                        localCache.add(sourceIndex, source.get(sourceIndex));
                    } else if(changeType == ListEvent.UPDATE) {
                        localCache.set(sourceIndex, source.get(sourceIndex));
                    }
                }
            } catch(RuntimeException e) {
                throw new RuntimeException("Failed to apply changes "
+ listChanges + " to " + before + ", source is " + source, e);
            }
        }

It looks like the ListEventAssembler is breaking somehow.
The problem is that it should be combining some events -
an insert followed by a delete - and it's not doing that:
java.lang.RuntimeException: Failed to apply changes ListEvent: [I.0-7,
D.0-82] to [9, 8, ....], source is []

Hopefully I'll be able to reproduce this problem in a single-threaded
test - although the bug gets caught by ThreadProxyEventList,
I believe its source is elsewhere. I'll keep you posted!

Cheers,
Jesse (or as my sister mocks me, "Mr. Glazed Lists")

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

RE: ThreadProxyEventList

Merki Fabian (KETO 41)
In reply to this post by Merki Fabian (KETO 41)
hi jesse

i don't think the problem lies elsewhere.
i think it's the concept of the ThreadProxyEventList. think about the followin scenario:

background thread (BT), gui thread (GT)
BT adds first row -> updates.forwardEvent() -> triggers GT via invokeLater
BT removes first row -> updates.forwardEvent() -> doesn't need to trigger because of scheduled (which btw needs synchronization)
GT gets time -> runs UpdateRunner -> because list is empty again it won't find the first row anylonger.

a second case (which is only visible in the gui and therefore really hard to test and which might be a result of the first problem (plus its exception)) is the sometimes the repaint of the table doesn't happen correctly and rows at the end of the table stay there although they shouldn't. (row 0-5 visible, 8+9 too - i only have a printout on my desk but i can grantee that this happend!)

thanks for looking at the problem so quickly.

fabian



-----Original Message-----
From: Jesse Wilson [mailto:[hidden email]]
Sent: Dienstag, 9. August 2005 04:26
To: [hidden email]
Subject: Re: ThreadProxyEventList


Hey Fabian --

Thanks for your great test application, I was able to
quickly find & diagnose the problem. I made the following
change to ThreadProxyEventList to get to the root of the problem,

        /**
         * Update local state as a consequence of the change event.
         */
        public void listChanged(ListEvent listChanges) {
            List before = new ArrayList(localCache);
            try {
                while(listChanges.next()) {
                    int sourceIndex = listChanges.getIndex();
                    int changeType = listChanges.getType();
   
                    if(changeType == ListEvent.DELETE) {
                        localCache.remove(sourceIndex);
                    } else if(changeType == ListEvent.INSERT) {
                        localCache.add(sourceIndex, source.get(sourceIndex));
                    } else if(changeType == ListEvent.UPDATE) {
                        localCache.set(sourceIndex, source.get(sourceIndex));
                    }
                }
            } catch(RuntimeException e) {
                throw new RuntimeException("Failed to apply changes "
+ listChanges + " to " + before + ", source is " + source, e);
            }
        }

It looks like the ListEventAssembler is breaking somehow.
The problem is that it should be combining some events -
an insert followed by a delete - and it's not doing that:
java.lang.RuntimeException: Failed to apply changes ListEvent: [I.0-7,
D.0-82] to [9, 8, ....], source is []

Hopefully I'll be able to reproduce this problem in a single-threaded
test - although the bug gets caught by ThreadProxyEventList,
I believe its source is elsewhere. I'll keep you posted!

Cheers,
Jesse (or as my sister mocks me, "Mr. Glazed Lists")

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: ThreadProxyEventList

Jesse Wilson-2
Hey Fabian ---

Yeah we're both right.

ListEventAssembler knows how to combine two list events
into one list event. This is necessary for when ThreadProxyEventList
is forwarding multiple events, but it could be used in other places
other than ThreadProxyEventList. I think that right now however,
ThreadProxyEventList is the only class in Glazed Lists that uses
ListEventAssembler in this way!

Here's some examples of what ListEventAssembler can do for you,
usually completely automatically and unseen by the developer.
Suppose you have the list C,A,T
    add an element at 2, source is C,A,R,T
    remove an element at 0, source is A,R,T
Now when you combine these two events into one, you realize
that the element to be added at index 2 'R' is actually not at index
2 anymore because it was offset by the 'A'. So given C,A,T
and a source of A,R,T the directions are interpretted:
    add an element at 2, source is 'A,R,T', we become C,A,T,T'
    remove an element at 0, source is 'A,R,T', we become A,T,T
So as you can see, the add & remove instructions are broken
when combined from two one-part steps into one two-part step.
What ListEventAssembler does automatically is normalize events.
This means that the recipient of a ListEvent should always see
indices in increasing order, regardless of the order the event was
created. In this example, ListEventAssembler would change the
event to become
    remove an element at 0, source is A,R,T
    add an element at 1, source is A,R,T

This is one of four types of reductions performed by ListEventAssembler
to normalize your ListEvents so that the recipient can handle them most easily.
The types of reductions are:
    - removing contradicting events - ie. an insert gets removed
    - splitting blocks - ie. an insert within an update
    - combining blocks - ie. adjacent inserts or removes
    - swapping blocks - ie. ordering from smallest to greatest
And if you're curious, this code is in ListEventBlock.java,
https://glazedlists.dev.java.net/source/browse/glazedlists/source/ca/odell/glazedlists/event/ListEventBlock.java?view=markup

It appears that our problem is that in some case we're not removing the
contradicting part of an insert followed by a delete that removes the deleted
indices. I should be able to fix this problem this evening.

Cheers,
Jesse

On 8/9/05, Merki Fabian (KETO 41) <[hidden email]> wrote:

> hi jesse
>
> i don't think the problem lies elsewhere.
> i think it's the concept of the ThreadProxyEventList. think about the followin scenario:
>
> background thread (BT), gui thread (GT)
> BT adds first row -> updates.forwardEvent() -> triggers GT via invokeLater
> BT removes first row -> updates.forwardEvent() -> doesn't need to trigger because of scheduled (which btw needs synchronization)
> GT gets time -> runs UpdateRunner -> because list is empty again it won't find the first row anylonger.
>
> a second case (which is only visible in the gui and therefore really hard to test and which might be a result of the first problem (plus its exception)) is the sometimes the repaint of the table doesn't happen correctly and rows at the end of the table stay there although they shouldn't. (row 0-5 visible, 8+9 too - i only have a printout on my desk but i can grantee that this happend!)
>
> thanks for looking at the problem so quickly.
>
> fabian
>
>
>
> -----Original Message-----
> From: Jesse Wilson [mailto:[hidden email]]
> Sent: Dienstag, 9. August 2005 04:26
> To: [hidden email]
> Subject: Re: ThreadProxyEventList
>
>
> Hey Fabian --
>
> Thanks for your great test application, I was able to
> quickly find & diagnose the problem. I made the following
> change to ThreadProxyEventList to get to the root of the problem,
>
>         /**
>          * Update local state as a consequence of the change event.
>          */
>         public void listChanged(ListEvent listChanges) {
>             List before = new ArrayList(localCache);
>             try {
>                 while(listChanges.next()) {
>                     int sourceIndex = listChanges.getIndex();
>                     int changeType = listChanges.getType();
>
>                     if(changeType == ListEvent.DELETE) {
>                         localCache.remove(sourceIndex);
>                     } else if(changeType == ListEvent.INSERT) {
>                         localCache.add(sourceIndex, source.get(sourceIndex));
>                     } else if(changeType == ListEvent.UPDATE) {
>                         localCache.set(sourceIndex, source.get(sourceIndex));
>                     }
>                 }
>             } catch(RuntimeException e) {
>                 throw new RuntimeException("Failed to apply changes "
> + listChanges + " to " + before + ", source is " + source, e);
>             }
>         }
>
> It looks like the ListEventAssembler is breaking somehow.
> The problem is that it should be combining some events -
> an insert followed by a delete - and it's not doing that:
> java.lang.RuntimeException: Failed to apply changes ListEvent: [I.0-7,
> D.0-82] to [9, 8, ....], source is []
>
> Hopefully I'll be able to reproduce this problem in a single-threaded
> test - although the bug gets caught by ThreadProxyEventList,
> I believe its source is elsewhere. I'll keep you posted!
>
> Cheers,
> Jesse (or as my sister mocks me, "Mr. Glazed Lists")
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

RE: ThreadProxyEventList

Merki Fabian (KETO 41)
In reply to this post by Merki Fabian (KETO 41)
great news.
thanks
fabian

-----Original Message-----
From: Jesse Wilson [mailto:[hidden email]]
Sent: Dienstag, 9. August 2005 17:46
To: [hidden email]
Subject: Re: ThreadProxyEventList


Hey Fabian ---

Yeah we're both right.

ListEventAssembler knows how to combine two list events
into one list event. This is necessary for when ThreadProxyEventList
is forwarding multiple events, but it could be used in other places
other than ThreadProxyEventList. I think that right now however,
ThreadProxyEventList is the only class in Glazed Lists that uses
ListEventAssembler in this way!

Here's some examples of what ListEventAssembler can do for you,
usually completely automatically and unseen by the developer.
Suppose you have the list C,A,T
    add an element at 2, source is C,A,R,T
    remove an element at 0, source is A,R,T
Now when you combine these two events into one, you realize
that the element to be added at index 2 'R' is actually not at index
2 anymore because it was offset by the 'A'. So given C,A,T
and a source of A,R,T the directions are interpretted:
    add an element at 2, source is 'A,R,T', we become C,A,T,T'
    remove an element at 0, source is 'A,R,T', we become A,T,T
So as you can see, the add & remove instructions are broken
when combined from two one-part steps into one two-part step.
What ListEventAssembler does automatically is normalize events.
This means that the recipient of a ListEvent should always see
indices in increasing order, regardless of the order the event was
created. In this example, ListEventAssembler would change the
event to become
    remove an element at 0, source is A,R,T
    add an element at 1, source is A,R,T

This is one of four types of reductions performed by ListEventAssembler
to normalize your ListEvents so that the recipient can handle them most easily.
The types of reductions are:
    - removing contradicting events - ie. an insert gets removed
    - splitting blocks - ie. an insert within an update
    - combining blocks - ie. adjacent inserts or removes
    - swapping blocks - ie. ordering from smallest to greatest
And if you're curious, this code is in ListEventBlock.java,
https://glazedlists.dev.java.net/source/browse/glazedlists/source/ca/odell/glazedlists/event/ListEventBlock.java?view=markup

It appears that our problem is that in some case we're not removing the
contradicting part of an insert followed by a delete that removes the deleted
indices. I should be able to fix this problem this evening.

Cheers,
Jesse

On 8/9/05, Merki Fabian (KETO 41) <[hidden email]> wrote:

> hi jesse
>
> i don't think the problem lies elsewhere.
> i think it's the concept of the ThreadProxyEventList. think about the followin scenario:
>
> background thread (BT), gui thread (GT)
> BT adds first row -> updates.forwardEvent() -> triggers GT via invokeLater
> BT removes first row -> updates.forwardEvent() -> doesn't need to trigger because of scheduled (which btw needs synchronization)
> GT gets time -> runs UpdateRunner -> because list is empty again it won't find the first row anylonger.
>
> a second case (which is only visible in the gui and therefore really hard to test and which might be a result of the first problem (plus its exception)) is the sometimes the repaint of the table doesn't happen correctly and rows at the end of the table stay there although they shouldn't. (row 0-5 visible, 8+9 too - i only have a printout on my desk but i can grantee that this happend!)
>
> thanks for looking at the problem so quickly.
>
> fabian
>
>
>
> -----Original Message-----
> From: Jesse Wilson [mailto:[hidden email]]
> Sent: Dienstag, 9. August 2005 04:26
> To: [hidden email]
> Subject: Re: ThreadProxyEventList
>
>
> Hey Fabian --
>
> Thanks for your great test application, I was able to
> quickly find & diagnose the problem. I made the following
> change to ThreadProxyEventList to get to the root of the problem,
>
>         /**
>          * Update local state as a consequence of the change event.
>          */
>         public void listChanged(ListEvent listChanges) {
>             List before = new ArrayList(localCache);
>             try {
>                 while(listChanges.next()) {
>                     int sourceIndex = listChanges.getIndex();
>                     int changeType = listChanges.getType();
>
>                     if(changeType == ListEvent.DELETE) {
>                         localCache.remove(sourceIndex);
>                     } else if(changeType == ListEvent.INSERT) {
>                         localCache.add(sourceIndex, source.get(sourceIndex));
>                     } else if(changeType == ListEvent.UPDATE) {
>                         localCache.set(sourceIndex, source.get(sourceIndex));
>                     }
>                 }
>             } catch(RuntimeException e) {
>                 throw new RuntimeException("Failed to apply changes "
> + listChanges + " to " + before + ", source is " + source, e);
>             }
>         }
>
> It looks like the ListEventAssembler is breaking somehow.
> The problem is that it should be combining some events -
> an insert followed by a delete - and it's not doing that:
> java.lang.RuntimeException: Failed to apply changes ListEvent: [I.0-7,
> D.0-82] to [9, 8, ....], source is []
>
> Hopefully I'll be able to reproduce this problem in a single-threaded
> test - although the bug gets caught by ThreadProxyEventList,
> I believe its source is elsewhere. I'll keep you posted!
>
> Cheers,
> Jesse (or as my sister mocks me, "Mr. Glazed Lists")
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

RE: ThreadProxyEventList

Merki Fabian (KETO 41)
In reply to this post by Merki Fabian (KETO 41)
hi jesse

i've just looked at the ListEventAssembler and saw that eventLevel isn't always synchronized.

    public void forwardEvent(ListEvent listChanges) {
        // if we're not nested, we can fire the event directly
        if(eventLevel == 0) {

because the commitEvent is called in the gui thread (which than requires synchronization) this is a very likely bug.
also atomicChangeBlocks and allowContradictingEvents are used in the commitEvent which are other possible candidates.

i guess you are aware of the following - but just in case you're not:

when using multi cpu machines in particilar: accessing the same field from two diffrent thread can lead into ugly problems (like we have) because both threads work with a "copy" of the field. when one changes the field the other might still see the old value.

fabian


-----Original Message-----
From: Jesse Wilson [mailto:[hidden email]]
Sent: Dienstag, 9. August 2005 17:46
To: [hidden email]
Subject: Re: ThreadProxyEventList


Hey Fabian ---

Yeah we're both right.

ListEventAssembler knows how to combine two list events
into one list event. This is necessary for when ThreadProxyEventList
is forwarding multiple events, but it could be used in other places
other than ThreadProxyEventList. I think that right now however,
ThreadProxyEventList is the only class in Glazed Lists that uses
ListEventAssembler in this way!

Here's some examples of what ListEventAssembler can do for you,
usually completely automatically and unseen by the developer.
Suppose you have the list C,A,T
    add an element at 2, source is C,A,R,T
    remove an element at 0, source is A,R,T
Now when you combine these two events into one, you realize
that the element to be added at index 2 'R' is actually not at index
2 anymore because it was offset by the 'A'. So given C,A,T
and a source of A,R,T the directions are interpretted:
    add an element at 2, source is 'A,R,T', we become C,A,T,T'
    remove an element at 0, source is 'A,R,T', we become A,T,T
So as you can see, the add & remove instructions are broken
when combined from two one-part steps into one two-part step.
What ListEventAssembler does automatically is normalize events.
This means that the recipient of a ListEvent should always see
indices in increasing order, regardless of the order the event was
created. In this example, ListEventAssembler would change the
event to become
    remove an element at 0, source is A,R,T
    add an element at 1, source is A,R,T

This is one of four types of reductions performed by ListEventAssembler
to normalize your ListEvents so that the recipient can handle them most easily.
The types of reductions are:
    - removing contradicting events - ie. an insert gets removed
    - splitting blocks - ie. an insert within an update
    - combining blocks - ie. adjacent inserts or removes
    - swapping blocks - ie. ordering from smallest to greatest
And if you're curious, this code is in ListEventBlock.java,
https://glazedlists.dev.java.net/source/browse/glazedlists/source/ca/odell/glazedlists/event/ListEventBlock.java?view=markup

It appears that our problem is that in some case we're not removing the
contradicting part of an insert followed by a delete that removes the deleted
indices. I should be able to fix this problem this evening.

Cheers,
Jesse

On 8/9/05, Merki Fabian (KETO 41) <[hidden email]> wrote:

> hi jesse
>
> i don't think the problem lies elsewhere.
> i think it's the concept of the ThreadProxyEventList. think about the followin scenario:
>
> background thread (BT), gui thread (GT)
> BT adds first row -> updates.forwardEvent() -> triggers GT via invokeLater
> BT removes first row -> updates.forwardEvent() -> doesn't need to trigger because of scheduled (which btw needs synchronization)
> GT gets time -> runs UpdateRunner -> because list is empty again it won't find the first row anylonger.
>
> a second case (which is only visible in the gui and therefore really hard to test and which might be a result of the first problem (plus its exception)) is the sometimes the repaint of the table doesn't happen correctly and rows at the end of the table stay there although they shouldn't. (row 0-5 visible, 8+9 too - i only have a printout on my desk but i can grantee that this happend!)
>
> thanks for looking at the problem so quickly.
>
> fabian
>
>
>
> -----Original Message-----
> From: Jesse Wilson [mailto:[hidden email]]
> Sent: Dienstag, 9. August 2005 04:26
> To: [hidden email]
> Subject: Re: ThreadProxyEventList
>
>
> Hey Fabian --
>
> Thanks for your great test application, I was able to
> quickly find & diagnose the problem. I made the following
> change to ThreadProxyEventList to get to the root of the problem,
>
>         /**
>          * Update local state as a consequence of the change event.
>          */
>         public void listChanged(ListEvent listChanges) {
>             List before = new ArrayList(localCache);
>             try {
>                 while(listChanges.next()) {
>                     int sourceIndex = listChanges.getIndex();
>                     int changeType = listChanges.getType();
>
>                     if(changeType == ListEvent.DELETE) {
>                         localCache.remove(sourceIndex);
>                     } else if(changeType == ListEvent.INSERT) {
>                         localCache.add(sourceIndex, source.get(sourceIndex));
>                     } else if(changeType == ListEvent.UPDATE) {
>                         localCache.set(sourceIndex, source.get(sourceIndex));
>                     }
>                 }
>             } catch(RuntimeException e) {
>                 throw new RuntimeException("Failed to apply changes "
> + listChanges + " to " + before + ", source is " + source, e);
>             }
>         }
>
> It looks like the ListEventAssembler is breaking somehow.
> The problem is that it should be combining some events -
> an insert followed by a delete - and it's not doing that:
> java.lang.RuntimeException: Failed to apply changes ListEvent: [I.0-7,
> D.0-82] to [9, 8, ....], source is []
>
> Hopefully I'll be able to reproduce this problem in a single-threaded
> test - although the bug gets caught by ThreadProxyEventList,
> I believe its source is elsewhere. I'll keep you posted!
>
> Cheers,
> Jesse (or as my sister mocks me, "Mr. Glazed Lists")
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: ThreadProxyEventList

James Lemieux
In reply to this post by Merki Fabian (KETO 41)
Hey Fabian --

We've reproduced the ThreadProxyEventList problem without the
ThreadProxyEventList. Needless to say we haven't yet found the
simplest possible test that reproduces this problem. We're still
working on that.

The included TransactionalEventList allows us to tweak the
begin and end events, similar to what ThreadProxyEventList does.

The other code was generated by observing which changes occured
in your TableTest class. We've already got a fix but we want a concise
test case before we commit any code back to CVS.

Cheers,
James & Jesse


    public void testCombineEvents() {
        TransactionalEventList list = new TransactionalEventList(new BasicEventList());
        for (int i = 0; i < 16; i++)
             list.add(new Integer(0));

        list.addListEventListener(new ConsistencyTestList(list, "transactional", true));

        list.beginEvent();

        for(int i = 0; i < 4; i++) list.add(8, new Object());
        for(int j = 7; j >= 0; j--) {
            for(int i = 0; i < 10; i++) list.add(j, new Object());
        }
        list.remove(55);
        list.remove(95);
        list.remove(14);
        list.remove(22);
        list.remove(27);
        list.remove(78);
        list.remove(1);
        list.remove(85);
        list.remove(52);
        list.remove(14);
        list.remove(39);
        list.remove(38);
        list.remove(61);
        list.remove(69);
        list.remove(8);
        list.remove(57);
        list.remove(10);
        list.remove(5);
        list.remove(71);
        list.remove(60);
        list.remove(42);
        list.remove(21);
        list.remove(15);
        list.remove(59);
        list.remove(15);
        list.remove(14);
        list.remove(24);
        list.remove(43);
        list.remove(35);
        list.remove(12);
        list.remove(11);
        list.remove(34);
        list.remove(42);
        list.remove(32);
        list.remove(19);
        list.add(32, new Integer(92));
        list.remove(44);
        list.remove(19);
        list.remove(45);
        list.remove(55);
        list.remove(23);
        list.remove(11);
        list.remove(8);
        list.remove(50);
        list.remove(29);
        list.remove(31);
        list.remove(33);
        list.remove(45);
        list.remove(15);
        list.remove(25);
        list.remove(8);
        list.add(40, new Integer(95));
        list.remove(32);
        list.remove(3);
        list.remove(26);
        list.remove(14);
        list.remove(36);
        list.add(39, new Integer(96));
        list.remove(34);
        list.remove(21);
        list.remove(13);
        list.remove(32);
        list.remove(30);
        list.add(36, new Integer(97));
        list.remove(43);
        list.remove(2);
        list.remove(34);
        list.remove(35);
        list.remove(17);
        list.add(39, new Integer(98));
        for(int i = 0; i < 5; i++) {
            list.remove(list.size() - 1);
        }
        list.add(29, new Integer(99));
        for(int i = 0; i < 5; i++) {
            list.remove(list.size() - 1);
        }
        list.add(22, new Integer(100));
        for(int i = 0; i < 5; i++) {
            list.remove(list.size() - 1);
        }
        list.set(25, new Integer(101)); // critical
        for(int j = 0; j < 4; j++) {
            for(int i = 0; i < 5; i++) list.remove(0);
            list.add(0, new Integer(102));
        }
        for(int i = 0; i < 10; i++) list.remove(0);
        list.add(0, new Integer(107));
        for(int i = 0; i < 2; i++) list.remove(0);

        list.commitEvent();
    }

    private static class TransactionalEventList extends TransformedList {
        public TransactionalEventList(EventList source) {
            super(source);
            source.addListEventListener(this);
        }

        public void listChanged(ListEvent listChanges) {
            updates.forwardEvent(listChanges);
        }

        public void beginEvent() {
            updates.beginEvent(true);
        }

        public void commitEvent() {
            updates.commitEvent();
        }

        protected boolean isWritable() {
            return true;
        }
    }


On 8/9/05, Merki Fabian (KETO 41) <[hidden email]> wrote:
great news.
thanks
fabian

-----Original Message-----
From: Jesse Wilson [mailto:[hidden email]]
Sent: Dienstag, 9. August 2005 17:46
To: [hidden email]
Subject: Re: ThreadProxyEventList


Hey Fabian ---

Yeah we're both right.

ListEventAssembler knows how to combine two list events
into one list event. This is necessary for when ThreadProxyEventList
is forwarding multiple events, but it could be used in other places
other than ThreadProxyEventList. I think that right now however,
ThreadProxyEventList is the only class in Glazed Lists that uses
ListEventAssembler in this way!

Here's some examples of what ListEventAssembler can do for you,
usually completely automatically and unseen by the developer.
Suppose you have the list C,A,T
    add an element at 2, source is C,A,R,T
    remove an element at 0, source is A,R,T
Now when you combine these two events into one, you realize
that the element to be added at index 2 'R' is actually not at index
2 anymore because it was offset by the 'A'. So given C,A,T
and a source of A,R,T the directions are interpretted:
    add an element at 2, source is 'A,R,T', we become C,A,T,T'
    remove an element at 0, source is 'A,R,T', we become A,T,T
So as you can see, the add & remove instructions are broken
when combined from two one-part steps into one two-part step.
What ListEventAssembler does automatically is normalize events.
This means that the recipient of a ListEvent should always see
indices in increasing order, regardless of the order the event was
created. In this example, ListEventAssembler would change the
event to become
    remove an element at 0, source is A,R,T
    add an element at 1, source is A,R,T

This is one of four types of reductions performed by ListEventAssembler
to normalize your ListEvents so that the recipient can handle them most easily.
The types of reductions are:
    - removing contradicting events - ie. an insert gets removed
    - splitting blocks - ie. an insert within an update
    - combining blocks - ie. adjacent inserts or removes
    - swapping blocks - ie. ordering from smallest to greatest
And if you're curious, this code is in ListEventBlock.java,
https://glazedlists.dev.java.net/source/browse/glazedlists/source/ca/odell/glazedlists/event/ListEventBlock.java?view=markup

It appears that our problem is that in some case we're not removing the
contradicting part of an insert followed by a delete that removes the deleted
indices. I should be able to fix this problem this evening.

Cheers,
Jesse

On 8/9/05, Merki Fabian (KETO 41) <[hidden email]> wrote:

> hi jesse
>
> i don't think the problem lies elsewhere.
> i think it's the concept of the ThreadProxyEventList. think about the followin scenario:
>
> background thread (BT), gui thread (GT)
> BT adds first row -> updates.forwardEvent() -> triggers GT via invokeLater
> BT removes first row -> updates.forwardEvent() -> doesn't need to trigger because of scheduled (which btw needs synchronization)
> GT gets time -> runs UpdateRunner -> because list is empty again it won't find the first row anylonger.
>
> a second case (which is only visible in the gui and therefore really hard to test and which might be a result of the first problem (plus its exception)) is the sometimes the repaint of the table doesn't happen correctly and rows at the end of the table stay there although they shouldn't. (row 0-5 visible, 8+9 too - i only have a printout on my desk but i can grantee that this happend!)
>
> thanks for looking at the problem so quickly.
>
> fabian
>
>
>
> -----Original Message-----
> From: Jesse Wilson [mailto: [hidden email]]
> Sent: Dienstag, 9. August 2005 04:26
> To: [hidden email]
> Subject: Re: ThreadProxyEventList
>
>
> Hey Fabian --
>
> Thanks for your great test application, I was able to
> quickly find & diagnose the problem. I made the following
> change to ThreadProxyEventList to get to the root of the problem,
>
>         /**
>          * Update local state as a consequence of the change event.
>          */
>         public void listChanged(ListEvent listChanges) {
>             List before = new ArrayList(localCache);
>             try {
>                 while(listChanges.next()) {
>                     int sourceIndex = listChanges.getIndex();
>                     int changeType = listChanges.getType();
>
>                     if(changeType == ListEvent.DELETE) {
>                         localCache.remove(sourceIndex);
>                     } else if(changeType == ListEvent.INSERT) {
>                         localCache.add(sourceIndex, source.get(sourceIndex));
>                     } else if(changeType == ListEvent.UPDATE) {
>                         localCache.set(sourceIndex, source.get(sourceIndex));
>                     }
>                 }
>             } catch(RuntimeException e) {
>                 throw new RuntimeException("Failed to apply changes "
> + listChanges + " to " + before + ", source is " + source, e);
>             }
>         }
>
> It looks like the ListEventAssembler is breaking somehow.
> The problem is that it should be combining some events -
> an insert followed by a delete - and it's not doing that:
> java.lang.RuntimeException: Failed to apply changes ListEvent: [I.0-7,
> D.0-82 ] to [9, 8, ....], source is []
>
> Hopefully I'll be able to reproduce this problem in a single-threaded
> test - although the bug gets caught by ThreadProxyEventList,
> I believe its source is elsewhere. I'll keep you posted!
>
> Cheers,
> Jesse (or as my sister mocks me, "Mr. Glazed Lists")
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]


Reply | Threaded
Open this post in threaded view
|

RE: ThreadProxyEventList

Merki Fabian (KETO 41)
In reply to this post by Merki Fabian (KETO 41)
hi guys
 
just wanted to ask you the current state about this problem...
 
thanks
fabian
-----Original Message-----
From: James Lemieux [mailto:[hidden email]]
Sent: Mittwoch, 10. August 2005 08:52
To: [hidden email]
Subject: Re: ThreadProxyEventList

Hey Fabian --

We've reproduced the ThreadProxyEventList problem without the
ThreadProxyEventList. Needless to say we haven't yet found the
simplest possible test that reproduces this problem. We're still
working on that.

The included TransactionalEventList allows us to tweak the
begin and end events, similar to what ThreadProxyEventList does.

The other code was generated by observing which changes occured
in your TableTest class. We've already got a fix but we want a concise
test case before we commit any code back to CVS.

Cheers,
James & Jesse


    public void testCombineEvents() {
        TransactionalEventList list = new TransactionalEventList(new BasicEventList());
        for (int i = 0; i < 16; i++)
             list.add(new Integer(0));

        list.addListEventListener(new ConsistencyTestList(list, "transactional", true));

        list.beginEvent();

        for(int i = 0; i < 4; i++) list.add(8, new Object());
        for(int j = 7; j >= 0; j--) {
            for(int i = 0; i < 10; i++) list.add(j, new Object());
        }
        list.remove(55);
        list.remove(95);
        list.remove(14);
        list.remove(22);
        list.remove(27);
        list.remove(78);
        list.remove(1);
        list.remove(85);
        list.remove(52);
        list.remove(14);
        list.remove(39);
        list.remove(38);
        list.remove(61);
        list.remove(69);
        list.remove(8);
        list.remove(57);
        list.remove(10);
        list.remove(5);
        list.remove(71);
        list.remove(60);
        list.remove(42);
        list.remove(21);
        list.remove(15);
        list.remove(59);
        list.remove(15);
        list.remove(14);
        list.remove(24);
        list.remove(43);
        list.remove(35);
        list.remove(12);
        list.remove(11);
        list.remove(34);
        list.remove(42);
        list.remove(32);
        list.remove(19);
        list.add(32, new Integer(92));
        list.remove(44);
        list.remove(19);
        list.remove(45);
        list.remove(55);
        list.remove(23);
        list.remove(11);
        list.remove(8);
        list.remove(50);
        list.remove(29);
        list.remove(31);
        list.remove(33);
        list.remove(45);
        list.remove(15);
        list.remove(25);
        list.remove(8);
        list.add(40, new Integer(95));
        list.remove(32);
        list.remove(3);
        list.remove(26);
        list.remove(14);
        list.remove(36);
        list.add(39, new Integer(96));
        list.remove(34);
        list.remove(21);
        list.remove(13);
        list.remove(32);
        list.remove(30);
        list.add(36, new Integer(97));
        list.remove(43);
        list.remove(2);
        list.remove(34);
        list.remove(35);
        list.remove(17);
        list.add(39, new Integer(98));
        for(int i = 0; i < 5; i++) {
            list.remove(list.size() - 1);
        }
        list.add(29, new Integer(99));
        for(int i = 0; i < 5; i++) {
            list.remove(list.size() - 1);
        }
        list.add(22, new Integer(100));
        for(int i = 0; i < 5; i++) {
            list.remove(list.size() - 1);
        }
        list.set(25, new Integer(101)); // critical
        for(int j = 0; j < 4; j++) {
            for(int i = 0; i < 5; i++) list.remove(0);
            list.add(0, new Integer(102));
        }
        for(int i = 0; i < 10; i++) list.remove(0);
        list.add(0, new Integer(107));
        for(int i = 0; i < 2; i++) list.remove(0);

        list.commitEvent();
    }

    private static class TransactionalEventList extends TransformedList {
        public TransactionalEventList(EventList source) {
            super(source);
            source.addListEventListener(this);
        }

        public void listChanged(ListEvent listChanges) {
            updates.forwardEvent(listChanges);
        }

        public void beginEvent() {
            updates.beginEvent(true);
        }

        public void commitEvent() {
            updates.commitEvent();
        }

        protected boolean isWritable() {
            return true;
        }
    }


On 8/9/05, Merki Fabian (KETO 41) <[hidden email]> wrote:
great news.
thanks
fabian

-----Original Message-----
From: Jesse Wilson [mailto:[hidden email]]
Sent: Dienstag, 9. August 2005 17:46
To: [hidden email]
Subject: Re: ThreadProxyEventList


Hey Fabian ---

Yeah we're both right.

ListEventAssembler knows how to combine two list events
into one list event. This is necessary for when ThreadProxyEventList
is forwarding multiple events, but it could be used in other places
other than ThreadProxyEventList. I think that right now however,
ThreadProxyEventList is the only class in Glazed Lists that uses
ListEventAssembler in this way!

Here's some examples of what ListEventAssembler can do for you,
usually completely automatically and unseen by the developer.
Suppose you have the list C,A,T
    add an element at 2, source is C,A,R,T
    remove an element at 0, source is A,R,T
Now when you combine these two events into one, you realize
that the element to be added at index 2 'R' is actually not at index
2 anymore because it was offset by the 'A'. So given C,A,T
and a source of A,R,T the directions are interpretted:
    add an element at 2, source is 'A,R,T', we become C,A,T,T'
    remove an element at 0, source is 'A,R,T', we become A,T,T
So as you can see, the add & remove instructions are broken
when combined from two one-part steps into one two-part step.
What ListEventAssembler does automatically is normalize events.
This means that the recipient of a ListEvent should always see
indices in increasing order, regardless of the order the event was
created. In this example, ListEventAssembler would change the
event to become
    remove an element at 0, source is A,R,T
    add an element at 1, source is A,R,T

This is one of four types of reductions performed by ListEventAssembler
to normalize your ListEvents so that the recipient can handle them most easily.
The types of reductions are:
    - removing contradicting events - ie. an insert gets removed
    - splitting blocks - ie. an insert within an update
    - combining blocks - ie. adjacent inserts or removes
    - swapping blocks - ie. ordering from smallest to greatest
And if you're curious, this code is in ListEventBlock.java,
https://glazedlists.dev.java.net/source/browse/glazedlists/source/ca/odell/glazedlists/event/ListEventBlock.java?view=markup

It appears that our problem is that in some case we're not removing the
contradicting part of an insert followed by a delete that removes the deleted
indices. I should be able to fix this problem this evening.

Cheers,
Jesse

On 8/9/05, Merki Fabian (KETO 41) <[hidden email]> wrote:

> hi jesse
>
> i don't think the problem lies elsewhere.
> i think it's the concept of the ThreadProxyEventList. think about the followin scenario:
>
> background thread (BT), gui thread (GT)
> BT adds first row -> updates.forwardEvent() -> triggers GT via invokeLater
> BT removes first row -> updates.forwardEvent() -> doesn't need to trigger because of scheduled (which btw needs synchronization)
> GT gets time -> runs UpdateRunner -> because list is empty again it won't find the first row anylonger.
>
> a second case (which is only visible in the gui and therefore really hard to test and which might be a result of the first problem (plus its exception)) is the sometimes the repaint of the table doesn't happen correctly and rows at the end of the table stay there although they shouldn't. (row 0-5 visible, 8+9 too - i only have a printout on my desk but i can grantee that this happend!)
>
> thanks for looking at the problem so quickly.
>
> fabian
>
>
>
> -----Original Message-----
> From: Jesse Wilson [mailto: [hidden email]]
> Sent: Dienstag, 9. August 2005 04:26
> To: [hidden email]
> Subject: Re: ThreadProxyEventList
>
>
> Hey Fabian --
>
> Thanks for your great test application, I was able to
> quickly find & diagnose the problem. I made the following
> change to ThreadProxyEventList to get to the root of the problem,
>
>         /**
>          * Update local state as a consequence of the change event.
>          */
>         public void listChanged(ListEvent listChanges) {
>             List before = new ArrayList(localCache);
>             try {
>                 while(listChanges.next()) {
>                     int sourceIndex = listChanges.getIndex();
>                     int changeType = listChanges.getType();
>
>                     if(changeType == ListEvent.DELETE) {
>                         localCache.remove(sourceIndex);
>                     } else if(changeType == ListEvent.INSERT) {
>                         localCache.add(sourceIndex, source.get(sourceIndex));
>                     } else if(changeType == ListEvent.UPDATE) {
>                         localCache.set(sourceIndex, source.get(sourceIndex));
>                     }
>                 }
>             } catch(RuntimeException e) {
>                 throw new RuntimeException("Failed to apply changes "
> + listChanges + " to " + before + ", source is " + source, e);
>             }
>         }
>
> It looks like the ListEventAssembler is breaking somehow.
> The problem is that it should be combining some events -
> an insert followed by a delete - and it's not doing that:
> java.lang.RuntimeException: Failed to apply changes ListEvent: [I.0-7,
> D.0-82 ] to [9, 8, ....], source is []
>
> Hopefully I'll be able to reproduce this problem in a single-threaded
> test - although the bug gets caught by ThreadProxyEventList,
> I believe its source is elsewhere. I'll keep you posted!
>
> Cheers,
> Jesse (or as my sister mocks me, "Mr. Glazed Lists")
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: ThreadProxyEventList

James Lemieux
Hey Fabian,

     Jesse and I have committed the fix to this problem to CVS and updated the latest CVS jar to include it. Here's the link:

https://glazedlists.dev.java.net/files/documents/1073/18783/glazedlists.jar

          As an aside, Jesse and I have been seriously consumed with the issue of supporting Glazed Lists simultaneously on 1.4 and 1.5 JVMs. We have made tremendous progress in the last few days. It is now only a matter of time, not engineering ability, before we can officially offer Glazing with JDK 1.5 features... namely generics! We are quite pleased with the elegance of the solution and we think you will be too. More to come in this area in the ensuing weeks.

keep your Lizzles Glizzled my nizzles,

James

On 8/14/05, Merki Fabian (KETO 41) <[hidden email]> wrote:
hi guys
 
just wanted to ask you the current state about this problem...
 
thanks
fabian
-----Original Message-----
From: James Lemieux [mailto:[hidden email]]
Sent: Mittwoch, 10. August 2005 08:52
To: [hidden email]
Subject: Re: ThreadProxyEventList

Hey Fabian --

We've reproduced the ThreadProxyEventList problem without the
ThreadProxyEventList. Needless to say we haven't yet found the
simplest possible test that reproduces this problem. We're still
working on that.

The included TransactionalEventList allows us to tweak the
begin and end events, similar to what ThreadProxyEventList does.

The other code was generated by observing which changes occured
in your TableTest class. We've already got a fix but we want a concise
test case before we commit any code back to CVS.

Cheers,
James & Jesse


    public void testCombineEvents() {
        TransactionalEventList list = new TransactionalEventList(new BasicEventList());
        for (int i = 0; i < 16; i++)
             list.add(new Integer(0));

        list.addListEventListener(new ConsistencyTestList(list, "transactional", true));

        list.beginEvent();

        for(int i = 0; i < 4; i++) list.add(8, new Object());
        for(int j = 7; j >= 0; j--) {
            for(int i = 0; i < 10; i++) list.add(j, new Object());
        }
        list.remove(55);
        list.remove(95);
        list.remove(14);
        list.remove(22);
        list.remove(27);
        list.remove(78);
        list.remove(1);
        list.remove(85);
        list.remove(52);
        list.remove(14);
        list.remove(39);
        list.remove(38);
        list.remove(61);
        list.remove(69);
        list.remove(8);
        list.remove(57);
        list.remove(10);
        list.remove(5);
        list.remove(71);
        list.remove(60);
        list.remove(42);
        list.remove(21);
        list.remove(15);
        list.remove(59);
        list.remove(15);
        list.remove(14);
        list.remove(24);
        list.remove(43);
        list.remove(35);
        list.remove(12);
        list.remove(11);
        list.remove(34);
        list.remove(42);
        list.remove(32);
        list.remove(19);
        list.add(32, new Integer(92));
        list.remove(44);
        list.remove(19);
        list.remove(45);
        list.remove(55);
        list.remove(23);
        list.remove(11);
        list.remove(8);
        list.remove(50);
        list.remove(29);
        list.remove(31);
        list.remove(33);
        list.remove(45);
        list.remove(15);
        list.remove(25);
        list.remove(8);
        list.add(40, new Integer(95));
        list.remove(32);
        list.remove(3);
        list.remove(26);
        list.remove(14);
        list.remove(36);
        list.add(39, new Integer(96));
        list.remove(34);
        list.remove(21);
        list.remove(13);
        list.remove(32);
        list.remove(30);
        list.add(36, new Integer(97));
        list.remove(43);
        list.remove(2);
        list.remove(34);
        list.remove(35);
        list.remove(17);
        list.add(39, new Integer(98));
        for(int i = 0; i < 5; i++) {
            list.remove(list.size() - 1);
        }
        list.add(29, new Integer(99));
        for(int i = 0; i < 5; i++) {
            list.remove(list.size() - 1);
        }
        list.add(22, new Integer(100));
        for(int i = 0; i < 5; i++) {
            list.remove(list.size() - 1);
        }
        list.set(25, new Integer(101)); // critical
        for(int j = 0; j < 4; j++) {
            for(int i = 0; i < 5; i++) list.remove(0);
            list.add(0, new Integer(102));
        }
        for(int i = 0; i < 10; i++) list.remove(0);
        list.add(0, new Integer(107));
        for(int i = 0; i < 2; i++) list.remove(0);

        list.commitEvent();
    }

    private static class TransactionalEventList extends TransformedList {
        public TransactionalEventList(EventList source) {
            super(source);
            source.addListEventListener(this);
        }

        public void listChanged(ListEvent listChanges) {
            updates.forwardEvent(listChanges);
        }

        public void beginEvent() {
            updates.beginEvent(true);
        }

        public void commitEvent() {
            updates.commitEvent();
        }

        protected boolean isWritable() {
            return true;
        }
    }


On 8/9/05, Merki Fabian (KETO 41) <[hidden email]> wrote:
great news.
thanks
fabian

-----Original Message-----
From: Jesse Wilson [mailto:[hidden email]]
Sent: Dienstag, 9. August 2005 17:46
To: [hidden email]
Subject: Re: ThreadProxyEventList


Hey Fabian ---

Yeah we're both right.

ListEventAssembler knows how to combine two list events
into one list event. This is necessary for when ThreadProxyEventList
is forwarding multiple events, but it could be used in other places
other than ThreadProxyEventList. I think that right now however,
ThreadProxyEventList is the only class in Glazed Lists that uses
ListEventAssembler in this way!

Here's some examples of what ListEventAssembler can do for you,
usually completely automatically and unseen by the developer.
Suppose you have the list C,A,T
    add an element at 2, source is C,A,R,T
    remove an element at 0, source is A,R,T
Now when you combine these two events into one, you realize
that the element to be added at index 2 'R' is actually not at index
2 anymore because it was offset by the 'A'. So given C,A,T
and a source of A,R,T the directions are interpretted:
    add an element at 2, source is 'A,R,T', we become C,A,T,T'
    remove an element at 0, source is 'A,R,T', we become A,T,T
So as you can see, the add & remove instructions are broken
when combined from two one-part steps into one two-part step.
What ListEventAssembler does automatically is normalize events.
This means that the recipient of a ListEvent should always see
indices in increasing order, regardless of the order the event was
created. In this example, ListEventAssembler would change the
event to become
    remove an element at 0, source is A,R,T
    add an element at 1, source is A,R,T

This is one of four types of reductions performed by ListEventAssembler
to normalize your ListEvents so that the recipient can handle them most easily.
The types of reductions are:
    - removing contradicting events - ie. an insert gets removed
    - splitting blocks - ie. an insert within an update
    - combining blocks - ie. adjacent inserts or removes
    - swapping blocks - ie. ordering from smallest to greatest
And if you're curious, this code is in ListEventBlock.java,
<a href="https://glazedlists.dev.java.net/source/browse/glazedlists/source/ca/odell/glazedlists/event/ListEventBlock.java?view=markup" target="_blank" onclick="return top.js.OpenExtLink(window,event,this)"> https://glazedlists.dev.java.net/source/browse/glazedlists/source/ca/odell/glazedlists/event/ListEventBlock.java?view=markup

It appears that our problem is that in some case we're not removing the
contradicting part of an insert followed by a delete that removes the deleted
indices. I should be able to fix this problem this evening.

Cheers,
Jesse

On 8/9/05, Merki Fabian (KETO 41) <[hidden email]> wrote:

> hi jesse
>
> i don't think the problem lies elsewhere.
> i think it's the concept of the ThreadProxyEventList. think about the followin scenario:
>
> background thread (BT), gui thread (GT)
> BT adds first row -> updates.forwardEvent() -> triggers GT via invokeLater
> BT removes first row -> updates.forwardEvent() -> doesn't need to trigger because of scheduled (which btw needs synchronization)
> GT gets time -> runs UpdateRunner -> because list is empty again it won't find the first row anylonger.
>
> a second case (which is only visible in the gui and therefore really hard to test and which might be a result of the first problem (plus its exception)) is the sometimes the repaint of the table doesn't happen correctly and rows at the end of the table stay there although they shouldn't. (row 0-5 visible, 8+9 too - i only have a printout on my desk but i can grantee that this happend!)
>
> thanks for looking at the problem so quickly.
>
> fabian
>
>
>
> -----Original Message-----
> From: Jesse Wilson [mailto: [hidden email]]
> Sent: Dienstag, 9. August 2005 04:26
> To: [hidden email]
> Subject: Re: ThreadProxyEventList
>
>
> Hey Fabian --
>
> Thanks for your great test application, I was able to
> quickly find & diagnose the problem. I made the following
> change to ThreadProxyEventList to get to the root of the problem,
>
>         /**
>          * Update local state as a consequence of the change event.
>          */
>         public void listChanged(ListEvent listChanges) {
>             List before = new ArrayList(localCache);
>             try {
>                 while(listChanges.next()) {
>                     int sourceIndex = listChanges.getIndex();
>                     int changeType = listChanges.getType();
>
>                     if(changeType == ListEvent.DELETE) {
>                         localCache.remove(sourceIndex);
>                     } else if(changeType == ListEvent.INSERT) {
>                         localCache.add(sourceIndex, source.get(sourceIndex));
>                     } else if(changeType == ListEvent.UPDATE) {
>                         localCache.set(sourceIndex, source.get(sourceIndex));
>                     }
>                 }
>             } catch(RuntimeException e) {
>                 throw new RuntimeException("Failed to apply changes "
> + listChanges + " to " + before + ", source is " + source, e);
>             }
>         }
>
> It looks like the ListEventAssembler is breaking somehow.
> The problem is that it should be combining some events -
> an insert followed by a delete - and it's not doing that:
> java.lang.RuntimeException: Failed to apply changes ListEvent: [I.0-7,
> D.0-82 ] to [9, 8, ....], source is []
>
> Hopefully I'll be able to reproduce this problem in a single-threaded
> test - although the bug gets caught by ThreadProxyEventList,
> I believe its source is elsewhere. I'll keep you posted!
>
> Cheers,
> Jesse (or as my sister mocks me, "Mr. Glazed Lists")
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]



Reply | Threaded
Open this post in threaded view
|

RE: ThreadProxyEventList

Merki Fabian (KETO 41)
In reply to this post by Merki Fabian (KETO 41)
good news, thanks. i'll test it in the next days.
 
what about my multi column sorter (see mail some days ago)? could you please put that in too? than i just could use the jar and wouldn't have to merge the source code...
 
fabian
-----Original Message-----
From: James Lemieux [mailto:[hidden email]]
Sent: Mittwoch, 17. August 2005 10:01
To: [hidden email]
Subject: Re: ThreadProxyEventList

Hey Fabian,

     Jesse and I have committed the fix to this problem to CVS and updated the latest CVS jar to include it. Here's the link:

https://glazedlists.dev.java.net/files/documents/1073/18783/glazedlists.jar

          As an aside, Jesse and I have been seriously consumed with the issue of supporting Glazed Lists simultaneously on 1.4 and 1.5 JVMs. We have made tremendous progress in the last few days. It is now only a matter of time, not engineering ability, before we can officially offer Glazing with JDK 1.5 features... namely generics! We are quite pleased with the elegance of the solution and we think you will be too. More to come in this area in the ensuing weeks.

keep your Lizzles Glizzled my nizzles,

James

On 8/14/05, Merki Fabian (KETO 41) <[hidden email]> wrote:
hi guys
 
just wanted to ask you the current state about this problem...
 
thanks
fabian
-----Original Message-----
From: James Lemieux [mailto:[hidden email]]
Sent: Mittwoch, 10. August 2005 08:52
To: [hidden email]
Subject: Re: ThreadProxyEventList

Hey Fabian --

We've reproduced the ThreadProxyEventList problem without the
ThreadProxyEventList. Needless to say we haven't yet found the
simplest possible test that reproduces this problem. We're still
working on that.

The included TransactionalEventList allows us to tweak the
begin and end events, similar to what ThreadProxyEventList does.

The other code was generated by observing which changes occured
in your TableTest class. We've already got a fix but we want a concise
test case before we commit any code back to CVS.

Cheers,
James & Jesse


    public void testCombineEvents() {
        TransactionalEventList list = new TransactionalEventList(new BasicEventList());
        for (int i = 0; i < 16; i++)
             list.add(new Integer(0));

        list.addListEventListener(new ConsistencyTestList(list, "transactional", true));

        list.beginEvent();

        for(int i = 0; i < 4; i++) list.add(8, new Object());
        for(int j = 7; j >= 0; j--) {
            for(int i = 0; i < 10; i++) list.add(j, new Object());
        }
        list.remove(55);
        list.remove(95);
        list.remove(14);
        list.remove(22);
        list.remove(27);
        list.remove(78);
        list.remove(1);
        list.remove(85);
        list.remove(52);
        list.remove(14);
        list.remove(39);
        list.remove(38);
        list.remove(61);
        list.remove(69);
        list.remove(8);
        list.remove(57);
        list.remove(10);
        list.remove(5);
        list.remove(71);
        list.remove(60);
        list.remove(42);
        list.remove(21);
        list.remove(15);
        list.remove(59);
        list.remove(15);
        list.remove(14);
        list.remove(24);
        list.remove(43);
        list.remove(35);
        list.remove(12);
        list.remove(11);
        list.remove(34);
        list.remove(42);
        list.remove(32);
        list.remove(19);
        list.add(32, new Integer(92));
        list.remove(44);
        list.remove(19);
        list.remove(45);
        list.remove(55);
        list.remove(23);
        list.remove(11);
        list.remove(8);
        list.remove(50);
        list.remove(29);
        list.remove(31);
        list.remove(33);
        list.remove(45);
        list.remove(15);
        list.remove(25);
        list.remove(8);
        list.add(40, new Integer(95));
        list.remove(32);
        list.remove(3);
        list.remove(26);
        list.remove(14);
        list.remove(36);
        list.add(39, new Integer(96));
        list.remove(34);
        list.remove(21);
        list.remove(13);
        list.remove(32);
        list.remove(30);
        list.add(36, new Integer(97));
        list.remove(43);
        list.remove(2);
        list.remove(34);
        list.remove(35);
        list.remove(17);
        list.add(39, new Integer(98));
        for(int i = 0; i < 5; i++) {
            list.remove(list.size() - 1);
        }
        list.add(29, new Integer(99));
        for(int i = 0; i < 5; i++) {
            list.remove(list.size() - 1);
        }
        list.add(22, new Integer(100));
        for(int i = 0; i < 5; i++) {
            list.remove(list.size() - 1);
        }
        list.set(25, new Integer(101)); // critical
        for(int j = 0; j < 4; j++) {
            for(int i = 0; i < 5; i++) list.remove(0);
            list.add(0, new Integer(102));
        }
        for(int i = 0; i < 10; i++) list.remove(0);
        list.add(0, new Integer(107));
        for(int i = 0; i < 2; i++) list.remove(0);

        list.commitEvent();
    }

    private static class TransactionalEventList extends TransformedList {
        public TransactionalEventList(EventList source) {
            super(source);
            source.addListEventListener(this);
        }

        public void listChanged(ListEvent listChanges) {
            updates.forwardEvent(listChanges);
        }

        public void beginEvent() {
            updates.beginEvent(true);
        }

        public void commitEvent() {
            updates.commitEvent();
        }

        protected boolean isWritable() {
            return true;
        }
    }


On 8/9/05, Merki Fabian (KETO 41) <[hidden email]> wrote:
great news.
thanks
fabian

-----Original Message-----
From: Jesse Wilson [mailto:[hidden email]]
Sent: Dienstag, 9. August 2005 17:46
To: [hidden email]
Subject: Re: ThreadProxyEventList


Hey Fabian ---

Yeah we're both right.

ListEventAssembler knows how to combine two list events
into one list event. This is necessary for when ThreadProxyEventList
is forwarding multiple events, but it could be used in other places
other than ThreadProxyEventList. I think that right now however,
ThreadProxyEventList is the only class in Glazed Lists that uses
ListEventAssembler in this way!

Here's some examples of what ListEventAssembler can do for you,
usually completely automatically and unseen by the developer.
Suppose you have the list C,A,T
    add an element at 2, source is C,A,R,T
    remove an element at 0, source is A,R,T
Now when you combine these two events into one, you realize
that the element to be added at index 2 'R' is actually not at index
2 anymore because it was offset by the 'A'. So given C,A,T
and a source of A,R,T the directions are interpretted:
    add an element at 2, source is 'A,R,T', we become C,A,T,T'
    remove an element at 0, source is 'A,R,T', we become A,T,T
So as you can see, the add & remove instructions are broken
when combined from two one-part steps into one two-part step.
What ListEventAssembler does automatically is normalize events.
This means that the recipient of a ListEvent should always see
indices in increasing order, regardless of the order the event was
created. In this example, ListEventAssembler would change the
event to become
    remove an element at 0, source is A,R,T
    add an element at 1, source is A,R,T

This is one of four types of reductions performed by ListEventAssembler
to normalize your ListEvents so that the recipient can handle them most easily.
The types of reductions are:
    - removing contradicting events - ie. an insert gets removed
    - splitting blocks - ie. an insert within an update
    - combining blocks - ie. adjacent inserts or removes
    - swapping blocks - ie. ordering from smallest to greatest
And if you're curious, this code is in ListEventBlock.java,
<A onclick="return top.js.OpenExtLink(window,event,this)" href="https://glazedlists.dev.java.net/source/browse/glazedlists/source/ca/odell/glazedlists/event/ListEventBlock.java?view=markup" target=_blank>https://glazedlists.dev.java.net/source/browse/glazedlists/source/ca/odell/glazedlists/event/ListEventBlock.java?view=markup

It appears that our problem is that in some case we're not removing the
contradicting part of an insert followed by a delete that removes the deleted
indices. I should be able to fix this problem this evening.

Cheers,
Jesse

On 8/9/05, Merki Fabian (KETO 41) <[hidden email]> wrote:

> hi jesse
>
> i don't think the problem lies elsewhere.
> i think it's the concept of the ThreadProxyEventList. think about the followin scenario:
>
> background thread (BT), gui thread (GT)
> BT adds first row -> updates.forwardEvent() -> triggers GT via invokeLater
> BT removes first row -> updates.forwardEvent() -> doesn't need to trigger because of scheduled (which btw needs synchronization)
> GT gets time -> runs UpdateRunner -> because list is empty again it won't find the first row anylonger.
>
> a second case (which is only visible in the gui and therefore really hard to test and which might be a result of the first problem (plus its exception)) is the sometimes the repaint of the table doesn't happen correctly and rows at the end of the table stay there although they shouldn't. (row 0-5 visible, 8+9 too - i only have a printout on my desk but i can grantee that this happend!)
>
> thanks for looking at the problem so quickly.
>
> fabian
>
>
>
> -----Original Message-----
> From: Jesse Wilson [mailto: [hidden email]]
> Sent: Dienstag, 9. August 2005 04:26
> To: [hidden email]
> Subject: Re: ThreadProxyEventList
>
>
> Hey Fabian --
>
> Thanks for your great test application, I was able to
> quickly find & diagnose the problem. I made the following
> change to ThreadProxyEventList to get to the root of the problem,
>
>         /**
>          * Update local state as a consequence of the change event.
>          */
>         public void listChanged(ListEvent listChanges) {
>             List before = new ArrayList(localCache);
>             try {
>                 while(listChanges.next()) {
>                     int sourceIndex = listChanges.getIndex();
>                     int changeType = listChanges.getType();
>
>                     if(changeType == ListEvent.DELETE) {
>                         localCache.remove(sourceIndex);
>                     } else if(changeType == ListEvent.INSERT) {
>                         localCache.add(sourceIndex, source.get(sourceIndex));
>                     } else if(changeType == ListEvent.UPDATE) {
>                         localCache.set(sourceIndex, source.get(sourceIndex));
>                     }
>                 }
>             } catch(RuntimeException e) {
>                 throw new RuntimeException("Failed to apply changes "
> + listChanges + " to " + before + ", source is " + source, e);
>             }
>         }
>
> It looks like the ListEventAssembler is breaking somehow.
> The problem is that it should be combining some events -
> an insert followed by a delete - and it's not doing that:
> java.lang.RuntimeException: Failed to apply changes ListEvent: [I.0-7,
> D.0-82 ] to [9, 8, ....], source is []
>
> Hopefully I'll be able to reproduce this problem in a single-threaded
> test - although the bug gets caught by ThreadProxyEventList,
> I believe its source is elsewhere. I'll keep you posted!
>
> Cheers,
> Jesse (or as my sister mocks me, "Mr. Glazed Lists")
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]