Re: [patch] Re: HashMap putAll/putAllInternal bug

2003-09-26 Thread Stuart Ballard
Brian Jones wrote:
Cool, don't want to hold you up on that too much.  Wondering how Sun's
impl makes a copy of the given Collection while avoiding copying an
object being modified in another thread.  Anyone know?  I think the
Iterators are supposed to throw exceptions when this occurs too.
I don't think this is affected too much either way by my patch. If 
another thread is actively modifying the collection being added at the 
time we're trying to add it, and it's not a thread-safe collection, the 
results are undefined. If it *is* a thread-safe collection, the most 
that's guaranteed is that you'll get a ConcurrentModificationException 
in the right place.

(Actually, my patch probably helps with that case too: by only 
incrementing this.size as each item is actually added, the state of the 
Map is closer to valid if a ConcurrentModificationException is thrown 
part-way through the iteration. Perhaps it could be tweaked to be even 
better in that regard by putting the size++ *after* the call to next()).

My patch really only affects what happens if the value returned by 
size() is incorrect for a particular Map. Without the patch, Classpath 
blindly believes size() and attempts to add exactly that many elements, 
throwing an exception if there are actually less and not adding all of 
them if there are actually more. With the patch, it relies on the 
iterator to determine how many items there actually are.

I have a map where it's prohibitively expensive[1] to calculate the 
correct value of size(). It would be possible, but a LOT of 
collections-using code assumes that size() is fast - the very code that 
this patch changes is an example, where it was assumed that calling 
size() was much faster than calling hasNext() for each element and 
therefore treated as an optimization. So the best I could do was to 
cache a best guess at the size and return that from size(), and have 
iterator() give the canonical information. On Sun's JDK, or on Classpath 
with the patch, this works. Without it, it doesn't.

Stuart.

[1] A correct implementation of size() for my map would actually look 
something like this:

public int size() {
  int size = 0;
  for (Iterator i = iterator(); i.hasNext(); ) {
i.next();
size++;
  }
  return size;
}
--
Stuart Ballard, Senior Web Developer
FASTNET - Web Solutions
(215) 283-2300, ext. 126
www.fast.net


___
Classpath mailing list
[EMAIL PROTECTED]
http://mail.gnu.org/mailman/listinfo/classpath


Re: [patch] Re: HashMap putAll/putAllInternal bug

2003-09-25 Thread Dalibor Topic
Stuart Ballard wrote:
Stuart Ballard wrote:

The HashMap putAll and putAllInternal (called from constructor) 
methods use size() to get the size of the map to be added, and then 
iterate over the iterator that many times to add elements. Instead, 
they should call hasNext() on the iterator.


Attached is a patch to HashMap and Hashtable to fix this issue.
Not attached here ;)

If someone would test and apply this patch, I'd be extremely grateful. 
(my paperwork was completed years ago, in case it matters - this patch 
is probably too small to care, anyway). The patch is untested as I've 
never been able to successfully build Classpath from source.
Could you give it spin on kaffe from CVS? It uses Classpath's collection 
classes.

cheers,
dalibor topic


___
Classpath mailing list
[EMAIL PROTECTED]
http://mail.gnu.org/mailman/listinfo/classpath


Re: [patch] Re: HashMap putAll/putAllInternal bug

2003-09-25 Thread Stuart Ballard
Dalibor Topic wrote:
Not attached here ;)
I already resent it but just in case that didn't make it through, I've 
re-attached it here.

Could you give it spin on kaffe from CVS? It uses Classpath's collection 
classes.
It applies cleanly to Kaffe CVS (of course) and passes all 144 make 
check tests; I also confirmed that it does have the desired behavior of 
allowing nrdo's first pass to run unmodified on Kaffe and generate 
correct results. If this gets accepted (and barring any problems in the 
second phase, which is mostly pretty simple JDBC) I can put nrdo on 
Savannah! Woo!

Stuart.

--
Stuart Ballard, Senior Web Developer
FASTNET - Web Solutions
(215) 283-2300, ext. 126
www.fast.net
Index: HashMap.java
===
RCS file: /cvsroot/classpath/classpath/java/util/HashMap.java,v
retrieving revision 1.24
diff -u -r1.24 HashMap.java
--- HashMap.java1 Aug 2003 03:31:32 -   1.24
+++ HashMap.java25 Sep 2003 18:16:52 -
@@ -381,8 +381,7 @@
   public void putAll(Map m)
   {
 Iterator itr = m.entrySet().iterator();
-int msize = m.size();
-while (msize--  0)
+while (itr.hasNext())
   {
 Map.Entry e = (Map.Entry) itr.next();
 // Optimize in case the Entry is one of our own.
@@ -709,10 +708,10 @@
   void putAllInternal(Map m)
   {
 Iterator itr = m.entrySet().iterator();
-int msize = m.size();
-size = msize;
-while (msize--  0)
+size = 0;
+while (itr.hasNext())
   {
+size++;
Map.Entry e = (Map.Entry) itr.next();
Object key = e.getKey();
int idx = hash(key);
Index: Hashtable.java
===
RCS file: /cvsroot/classpath/classpath/java/util/Hashtable.java,v
retrieving revision 1.28
diff -u -r1.28 Hashtable.java
--- Hashtable.java  7 May 2002 05:13:05 -   1.28
+++ Hashtable.java  25 Sep 2003 18:16:52 -
@@ -510,7 +510,7 @@
   {
 Iterator itr = m.entrySet().iterator();
 
-for (int msize = m.size(); msize  0; msize--)
+while (itr.hasNext())
   {
 Map.Entry e = (Map.Entry) itr.next();
 // Optimize in case the Entry is one of our own.
@@ -859,11 +859,11 @@
   void putAllInternal(Map m)
   {
 Iterator itr = m.entrySet().iterator();
-int msize = m.size();
-this.size = msize;
+size = 0;
 
-for (; msize  0; msize--)
+while (itr.hasNext())
   {
+size++;
Map.Entry e = (Map.Entry) itr.next();
Object key = e.getKey();
int idx = hash(key);
___
Classpath mailing list
[EMAIL PROTECTED]
http://mail.gnu.org/mailman/listinfo/classpath


Re: [patch] Re: HashMap putAll/putAllInternal bug

2003-09-25 Thread Brian Jones
Stuart Ballard [EMAIL PROTECTED] writes:

 Dalibor Topic wrote:
  Not attached here ;)
 
 I already resent it but just in case that didn't make it through, I've
 re-attached it here.
 
  Could you give it spin on kaffe from CVS? It uses Classpath's
  collection classes.
 
 It applies cleanly to Kaffe CVS (of course) and passes all 144 make
 check tests; I also confirmed that it does have the desired behavior
 of allowing nrdo's first pass to run unmodified on Kaffe and generate
 correct results. If this gets accepted (and barring any problems in
 the second phase, which is mostly pretty simple JDBC) I can put nrdo
 on Savannah! Woo!

Cool, don't want to hold you up on that too much.  Wondering how Sun's
impl makes a copy of the given Collection while avoiding copying an
object being modified in another thread.  Anyone know?  I think the
Iterators are supposed to throw exceptions when this occurs too.

Brian
--
Brian Jones [EMAIL PROTECTED]


___
Classpath mailing list
[EMAIL PROTECTED]
http://mail.gnu.org/mailman/listinfo/classpath


RE: [patch] Re: HashMap putAll/putAllInternal bug

2003-09-25 Thread David Holmes
Brian Jones wrote:
 Wondering how Sun's
 impl makes a copy of the given Collection while avoiding copying an
 object being modified in another thread.  Anyone know?  I think the
 Iterators are supposed to throw exceptions when this occurs too.

Most collections are not defined as being thread-safe. Even the
synchronized collections require that the user lock the collection
before iterating through it. Bottom line: It is up to the user to
ensure that a collection that is used with addAll etc is not being
modified whilst it is being added.

The ConcurrentModificationException thrown by Iterators is not just
for detecting concurrent modification by different threads (which they
may detect) but also for detecting modification of the collection by
the same thread that has an iterator active at the time the
modification is made. In simple terms they detect if you add while
iterating, or remove while iterating - other than via the iterators
remove() method.

David Holmes



___
Classpath mailing list
[EMAIL PROTECTED]
http://mail.gnu.org/mailman/listinfo/classpath