RR: ConcurrentModificationException: worth the overhead in web-mode?

15 views
Skip to first unread message

Henry Crutcher

unread,
Jan 3, 2007, 9:59:35 AM1/3/07
to Google-Web-Tool...@googlegroups.com
The question here is simple:  Is the extra run-time checking offered by Sun's fail-fast iterators worth the extra overhead in a browser context?  If this were added to web mode, collections would throw ConcurrentModificationException when iterated using an iterator after a modification occurs.

Pros:
    Conforms to Sun standard -- Having things behave as users expect is helpful.  On the other hand, the Javadocs state that "fail-fast behavior is not to be depended on for program flow, but only used as a bug-catching tool".  This seems to mean that this is only for debugging, not production, and web-mode is designed to be used in production only.  For instance, web-mode does not produce stack traces for exceptions.
     Makes web mode behave like hosted mode.

Cons:
      Creates small extra overhead in web mode -- Making this change would slow down all iteration in web mode, which would make all production apps slower.  As one of GWT's mandates is to be as fast as hand-written code, this could be a problem.
       Makes iterators use slightly more memory -- All iterators and data structures need to keep track of modification counts, which adds memory usage to all production apps. 
       Not designed for this environment -- Fail-fast iterators are designed to help catch multithreading bugs, but as browsers are all single threaded, this win is not relevant for the browser case.
      
    On looking at it, I'd like to get an idea how people would feel if GWT collections continued not being fail-fast.  Hosted mode collections, of course, would still be fail-fast as they are being taken from the JRE, but production environments would remain fast and small.


                 Thanks,

                       Henry

Scott Stirling

unread,
Jan 3, 2007, 10:28:08 AM1/3/07
to Google-Web-Tool...@googlegroups.com
I'd like to see a doc, like a "Thinking in GWT," that gives an
overview of the departures that GWT takes from Java and why they make
sense. I have given this input to a couple book authors when reviewing
their books for the publisher. But it's a tall order for someone to
produce unless, I project, they have worked closely with GWT for a
while, paid attention to this particular phenomenon, and have some
familiarity with the design choices implemented in the
Java->Javascript compiler.

Scott Stirling
Framingham, MA

Sandy McArthur

unread,
Jan 3, 2007, 12:37:44 PM1/3/07
to Google Web Toolkit Contributors

Henry Crutcher wrote:
> Cons:

> Not designed for this environment -- Fail-fast iterators are designed
> to help catch multithreading bugs, but as browsers are all single threaded,
> this win is not relevant for the browser case.

Fail fast iterators can detect problems in single threaded environments
too. A while ago I wrote some code that failed with
ConcurrentModificationException in hosted mode but not web mode.

The problem was I had a list of event listeners and in one listener I
sometimes removed another listener modifying that list. When I called
iterator.next to get the next listener the iterator threw a
ConcurrentModificationException in hosted mode. Had I not caught this,
in web mode I would have been not firing an event to a listener when I
should be in some situations.

> On looking at it, I'd like to get an idea how people would feel if GWT
> collections continued not being fail-fast. Hosted mode collections, of
> course, would still be fail-fast as they are being taken from the JRE, but
> production environments would remain fast and small.

I have mixed feelings on this. Is hosted mode supposed to be a
sufficient testing environment for everyone?

Also, is the cons of the fail-fast iterator something that can be
addressed with improvements to the GWTCompiler and what it can optimize
out of the generated JavaScript?

Henry Crutcher

unread,
Jan 3, 2007, 1:07:34 PM1/3/07
to Google-Web-Tool...@googlegroups.com
(This section left intentionally blank.)

On 1/3/07, Sandy McArthur <sand...@gmail.com> wrote:


Henry Crutcher wrote:
> Cons:
>        Not designed for this environment -- Fail-fast iterators are designed
> to help catch multithreading bugs, but as browsers are all single threaded,
> this win is not relevant for the browser case.

Fail fast iterators can detect problems in single threaded environments
too. A while ago I wrote some code that failed with
ConcurrentModificationException in hosted mode but not web mode.
The problem was I had a list of event listeners and in one listener I
sometimes removed another listener modifying that list. When I called
iterator.next to get the next listener the iterator threw a
ConcurrentModificationException in hosted mode. Had I not caught this,
in web mode I would have been not firing an event to a listener when I
should be in some situations.

In production, what sort of behavior would you have wanted to see if the bug had gone unnoticed? 

>     On looking at it, I'd like to get an idea how people would feel if GWT
> collections continued not being fail-fast.  Hosted mode collections, of
> course, would still be fail-fast as they are being taken from the JRE, but
> production environments would remain fast and small.

I have mixed feelings on this. Is hosted mode supposed to be a
sufficient testing environment for everyone?

That definitely bears on the question.  I'd love to know how many people do not use hosted mode for testing, or have to deal with a program where much of the control flow only happens in web mode.  If that number is too large, that may be interesting in its own right....
 

Also, is the cons of the fail-fast iterator something that can be
addressed with improvements to the GWTCompiler and what it can optimize
out of the generated JavaScript?

This would be difficult for a compiler writer to do, given that it is very data dependent, and could propagate rapidly if iterators are passed around, stored, etc.  Perhaps some sort of escape analysis could get one 20% of the way there, but that seems like it could easily cause its own problems.

         Henry

John Tamplin

unread,
Jan 3, 2007, 1:34:35 PM1/3/07
to Google-Web-Tool...@googlegroups.com
On 1/3/07, Henry Crutcher <h...@google.com> wrote:
Also, is the cons of the fail-fast iterator something that can be
addressed with improvements to the GWTCompiler and what it can optimize
out of the generated JavaScript?

This would be difficult for a compiler writer to do, given that it is very data dependent, and could propagate rapidly if iterators are passed around, stored, etc.  Perhaps some sort of escape analysis could get one 20% of the way there, but that seems like it could easily cause its own problems.

Personally, I would approach it more like whether to compile production code with -g, and have a compiler option (or property or whatever) that has the compiler generate code that provides the checks or not.  I haven't looked at this area, so it might even be possible to do it with deferred binding (ie, have collections classes that implement the exception and swap them in if desired by the programmer).

--
John A. Tamplin
Software Engineer, Google

Scott Blum

unread,
Jan 3, 2007, 1:43:11 PM1/3/07
to Google-Web-Tool...@googlegroups.com
On 1/3/07, Sandy McArthur <sand...@gmail.com> wrote:
Also, is the cons of the fail-fast iterator something that can be
addressed with improvements to the GWTCompiler and what it can optimize
out of the generated JavaScript?

Not any time soon; perhaps not even in theory.

The problem is the degree of what you're asking the compiler to figure out.  Consider this loop:

int x = 3;
int y = x;
for (int i = 0, c = 10; i < c; ++i) {
  if (x != y) {
    throw now ConcurrentModificationException();
  }
  // do some stuff
}

You want to optimize out the concurrent mode test.  First, you've got to have a compiler that's smart enough to even optimize the (x != y) comparison based on the fact that:
a) y was assigned to x
b) x has not changed since assignment
c) y has not changed since assignment

Then you have to prove that "do some stuff" cannot modify x and y.  This is a somewhat tractible problem when dealing with local variables.  Trying to solve it for fields in two objects would be a nightmare, if it's possible.

Now consider that the test actually lies inside of the implementation of next().  That means that to optimize out the test, you'd have to show that every call to next() for that class, everywhere in your program cannot throw the exception.

Scott

Sandy McArthur

unread,
Jan 3, 2007, 2:49:03 PM1/3/07
to Google Web Toolkit Contributors
Henry Crutcher wrote:
> On 1/3/07, Sandy McArthur <sand...@gmail.com> wrote:
> > Henry Crutcher wrote:
> > > Cons:
> > > Not designed for this environment -- Fail-fast iterators are
> > > designed
> > > to help catch multithreading bugs, but as browsers are all single
> > > threaded,
> > > this win is not relevant for the browser case.
> >
> > Fail fast iterators can detect problems in single threaded environments
> > too. A while ago I wrote some code that failed with
> > ConcurrentModificationException in hosted mode but not web mode.
> >
> > The problem was I had a list of event listeners and in one listener I
> > sometimes removed another listener modifying that list. When I called
> > iterator.next to get the next listener the iterator threw a
> > ConcurrentModificationException in hosted mode. Had I not caught this,
> > in web mode I would have been not firing an event to a listener when I
> > should be in some situations.
>
>
> In production, what sort of behavior would you have wanted to see if the bug
> had gone unnoticed?

In that particular situation the worst that would have happened is a
widget would have the wrong CSS styles set but I'd still prefer a
ConcurrentModificationException. I tend to have the conservative view
that prefers safety over speed which is one reason I like Java.

Say you had a List of messages from a mailbox or records in a database
loaded in the client's browser. The user issues a delete command on
items that match a criteria. Say the list is copied to another list,
then that list is iterated and some items are removed from it because
they shouldn't be deleted and the remaining items are sent via RPC back
to the server. If the programmer didn't use the iterator to remove the
items to be kept then without a fail-fast iterator items will be
deleted when the list is sent to the server.

I'd like to think I wouldn't do something like that again but I know
I've done equally stupid stuff in the past.

Mat Gessel

unread,
Jan 3, 2007, 11:01:08 PM1/3/07
to Google-Web-Tool...@googlegroups.com
This issue could be expanded to include client side assertions and
trace statements. I'm pretty neutral about fail fast iterators, but I
would be pleased if GWT had a debug compile mode. I created something
like this in GWT Tk using the ol' stub library/static removal combo.
Trace statements sure are nice when debugging mouse move events ;-)

The performance concerns could be addressed by isolating checks in
static-optimizable blocks.

for (int i = 0, c = 10; i < c; ++i) {

if (GWT.DEBUG && x != y) {


throw now ConcurrentModificationException();
}
// do some stuff
}

Perhaps the memory burden could be addressed by flagging fields to be
ignored by the compiler:

//@gwtdebug
int x = 3;

/**
* Blah blah blah
*
* @gwtdebug
*/
int y = x;

--
Mat Gessel
http://www.asquare.net/gwttk/

Miguel Méndez

unread,
Jan 4, 2007, 8:36:43 AM1/4/07
to Google-Web-Tool...@googlegroups.com
Ultimately, it is a developer's choice as to whether or not they wish to use a checked or unchecked JRE emulation library.  If we wanted to provide this distinction, the selection should be done as a dynamic binding decision.  This way a developer could choose to use a faster but unchecked version of the JRE or a slower but checked version.  In certain cases, the compiler maybe able to optimize the two into equivalent code but it is not a requirement.

Separately, the compiler should support targeting a debug or a release environment.  (This would mostly impact the code generation/optimization machinery within the compiler.)
--
Miguel

Emily Crutcher

unread,
Jan 4, 2007, 8:49:41 AM1/4/07
to Google-Web-Tool...@googlegroups.com
I know that Scott has been toying with adding client side assertions, could we add these checks in a assert() block when that happens?

Scott Blum

unread,
Jan 4, 2007, 9:42:21 AM1/4/07
to Google-Web-Tool...@googlegroups.com
On 1/3/07, Mat Gessel <mat.g...@gmail.com> wrote:
The performance concerns could be addressed by isolating checks in
static-optimizable blocks.

for (int i = 0, c = 10; i < c; ++i) {
  if (GWT.DEBUG && x != y) {
    throw now ConcurrentModificationException();
  }
  // do some stuff
}

Yep, that's a good idea.  Soon as we get if-then pruning in, that is. :)

Perhaps the memory burden could be addressed by flagging fields to be
ignored by the compiler:

//@gwtdebug
int x = 3;

/**
* Blah blah blah
*
* @gwtdebug
*/
int y = x;

Not necessary.  If all references are removed, the fields just disappear.

Scott

Miroslav Pokorny

unread,
Jan 6, 2007, 2:42:56 AM1/6/07
to Google-Web-Tool...@googlegroups.com
Lo

I would say the real reason for concurrent mod exception is to ensure iterators fail fast in order to ensure that the iterator isnt confused about where its up to when iterating and becomes incorrect.


eg Imagine an iterator going over an array.
* Deleting an item outside of the iterator would now cause the iterator to incorrectly skip an item.
* Deleting some items, the array shrinks and the index is out of range would result in an array index out of bounds exception.

In one case one never realised that an item was skipped which could be potentially serious, the other is nasty because the wrong exception is being thrown.

In both cases the thought in the design of the jdk was for a more serious reason - ie having correct software rather than making erroneous assumptions - eg not detecting modification detection..

My 3cents.
--
Reply all
Reply to author
Forward
0 new messages