Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8186171

HashMap: Entry.setValue may not work after Iterator.remove() called for previous entries

    Details

    • Subcomponent:
    • Resolved In Build:
      b26
    • CPU:
      generic
    • OS:
      generic
    • Verification:
      Verified

      Backports

        Description

        FULL PRODUCT VERSION :
        java version "1.8.0_144"
        Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
        Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)

        A DESCRIPTION OF THE PROBLEM :
        Javadoc allows to use Iterator.remove() and Entry.setValue() when iterating over HashMap:
        "If the map is modified while an iteration over the set is in progress (except through the iterator's own remove operation, or through the setValue operation on a map entry returned by the iterator) the results of the iteration are undefined."

        However it's possible that setValue() doesn't work if remove() was called for some of previous entries. It may happen when remove() leads to untreeify() call - some TreeNodes are replaced by Nodes, but iterator continues to iterate over old instances hence setValue changes the value in the replaced node. (Test case attached)

        REGRESSION. Last worked in version 7u80


        REPRODUCIBILITY :
        This bug can be reproduced always.
        JDK-8186171
        ---------- BEGIN SOURCE ----------
        import java.util.*;

        public class Main {
            static class Key {
                @Override
                public int hashCode() {
                    return 0; // to put keys in one bucket
                }
            }
            public static void main(String[] args) {
                List<Key> keys = new ArrayList<>();
                for (int i = 0; i < 11; ++i) { // 11 is big enough for a map to use tree nodes
                    keys.add(new Key());
                }
                Map<Object, Object> data = Collections.singletonMap(keys.get(10), 1);

                Map<Object, Object> result = new HashMap<>();
                for (Object key : keys) {
                    result.put(key, null);
                }

                for (Iterator<Map.Entry<Object, Object>> iter = result.entrySet().iterator(); iter.hasNext();) {
                    Map.Entry<Object, Object> entry = iter.next();
                    Object value = data.get(entry.getKey());
                    if (value == null) {
                        iter.remove();
                    } else {
                        entry.setValue(value); // this call doesn't set the value
                    }
                }

                if (result.containsValue(null)) {
                    System.out.println("FAILED");
                } else {
                    System.out.println("OK");
                }
            }
        }
        ---------- END SOURCE ----------

          Activity

          Hide
          psonal Pallavi Sonal added a comment -
          To reproduce the issue, run the attached test case.
          JDK 7u80 - Pass
          JDK 8u144- Fail
          JDK9-ea + 180- Fail
          Show
          psonal Pallavi Sonal added a comment - To reproduce the issue, run the attached test case. JDK 7u80 - Pass JDK 8u144- Fail JDK9-ea + 180- Fail
          Hide
          dl Doug Lea added a comment -
          There was a case where the "movable" argument was ignored in removeTreeNode

          diff ~/openjdk/jdk9/jdk/src/java.base/share/classes/java/util/HashMap.java .
          2092,2093c2092,2094
          < if (root == null || root.right == null ||
          < (rl = root.left) == null || rl.left == null) {
          ---
          > if (root == null ||
          > (movable && (root.right == null ||
          > (rl = root.left) == null || rl.left == null))) {
          Show
          dl Doug Lea added a comment - There was a case where the "movable" argument was ignored in removeTreeNode diff ~/openjdk/jdk9/jdk/src/java.base/share/classes/java/util/HashMap.java . 2092,2093c2092,2094 < if (root == null || root.right == null || < (rl = root.left) == null || rl.left == null) { --- > if (root == null || > (movable && (root.right == null || > (rl = root.left) == null || rl.left == null))) {
          Hide
          hgupdate HG Updates added a comment -
          URL: http://hg.openjdk.java.net/jdk10/master/rev/3f5f9bc0bdc2
          User: martin
          Date: 2017-10-03 21:43:43 +0000
          Show
          hgupdate HG Updates added a comment - URL: http://hg.openjdk.java.net/jdk10/master/rev/3f5f9bc0bdc2 User: martin Date: 2017-10-03 21:43:43 +0000

            People

            • Assignee:
              martin Martin Buchholz
              Reporter:
              webbuggrp Webbug Group
            • Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: