{"record":{"id":"4363da653684f9f2","repo":"apache/hadoop","slug":"modification-modification-itermodificat","errorCode":null,"errorMessage":"modification=\" + modification + \" != iterModification = \" + iterModification","messagePattern":"modification=\" \\+ modification \\+ \" != iterModification = \" \\+ iterModification","errorType":"exception","errorClass":"ConcurrentModificationException","httpStatus":null,"severity":"error","filePath":"hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/LightWeightGSet.java","lineNumber":314,"sourceCode":"\n  public class SetIterator implements Iterator<E> {\n    /** The starting modification for fail-fast. */\n    private int iterModification = modification;\n    /** The current index of the entry array. */\n    private int index = -1;\n    private LinkedElement cur = null;\n    private LinkedElement next = nextNonemptyEntry();\n    private boolean trackModification = true;\n\n    /** Find the next nonempty entry starting at (index + 1). */\n    private LinkedElement nextNonemptyEntry() {\n      for(index++; index < entries.length && entries[index] == null; index++);\n      return index < entries.length? entries[index]: null;\n    }\n\n    private void ensureNext() {\n      if (trackModification && modification != iterModification) {\n        throw new ConcurrentModificationException(\"modification=\" + modification\n            + \" != iterModification = \" + iterModification);\n      }\n      if (next != null) {\n        return;\n      }\n      if (cur == null) {\n        return;\n      }\n      next = cur.getNext();\n      if (next == null) {\n        next = nextNonemptyEntry();\n      }\n    }\n\n    @Override\n    public boolean hasNext() {\n      ensureNext();\n      return next != null;","sourceCodeStart":296,"sourceCodeEnd":332,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/LightWeightGSet.java#L296-L332","documentation":"The GSet iterator is fail-fast: every structural put()/remove() increments the set's modification counter, and the iterator compares it with the counter captured at creation (ensureNext), throwing ConcurrentModificationException('modification=X != iterModification = Y'). The set itself is not thread-safe, so mutations from another thread trip the same check.","triggerScenarios":"Mutating the set with put()/remove() while iterating values() - including from a callback fired inside the loop or from another thread.","commonSituations":"Expiry or eviction scans that delete entries during iteration; listener hooks mutating the same set mid-iteration; single-threaded maintenance code later moved into a background thread.","solutions":["Use iterator.remove() for in-loop deletion - it re-syncs iterModification after removing.","Two-phase update: collect targets during iteration, apply put/remove after the loop finishes.","For deliberate concurrent designs call setTrackModification(false) and add external locking, accepting that iteration may then observe skips or repeats."],"exampleFix":"// before\nfor (E e : gset.values()) {\n  if (isStale(e)) {\n    gset.remove(e.getKey()); // ConcurrentModificationException\n  }\n}\n\n// after\nList<K> stale = new ArrayList<>();\nfor (E e : gset.values()) {\n  if (isStale(e)) {\n    stale.add(e.getKey());\n  }\n}\nfor (K k : stale) {\n  gset.remove(k);\n}","handlingStrategy":"validation","validationCode":"// two-phase pattern: no structural change during iteration\nList<K> toRemove = new ArrayList<>();\nfor (E e : gset.values()) {\n  if (shouldEvict(e)) {\n    toRemove.add(e.getKey());\n  }\n}\ntoRemove.forEach(gset::remove);","typeGuard":null,"tryCatchPattern":"try {\n  for (E e : gset.values()) { ... }\n} catch (ConcurrentModificationException e) {\n  // restart the iteration on a snapshot; do not swallow and continue\n}","preventionTips":["Never call set.put/set.remove inside a loop over set.values(); use iterator.remove() or two-phase collect-then-mutate.","LightWeightGSet is not thread-safe: synchronize external access before sharing it across threads.","setTrackModification(false) disables the check - only use it with an external locking design."],"tags":["collections","iterator","concurrency","hadoop-common"],"backgroundTag":"concurrent-modification","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}