Project

Profile

Help

Bug #5106

Gizmo fails with ConcurrentModificationException using "delete //prefix:local~

Added by Michael Kay 24 days ago. Updated 21 days ago.

Status:
New
Priority:
Normal
Assignee:
Category:
Gizmo
Sprint/Milestone:
-
Start date:
2021-09-24
Due date:
% Done:

0%

Estimated time:
Legacy ID:
Applies to branch:
Fix Committed on Branch:
Fixed in Maintenance Release:
Platforms:

Description

For example

java -cp ~/bin/10.5/he/saxon-he-10.5.jar:/Users/mike/bin/10.5/he/jline-2.14.6.jar  net.sf.saxon.Gizmo
Saxon Gizmo 10.5
/>load /Users/mike/GitHub/saxon2020/src/samples/schemas/validation-reports.xsd
/>delete //xs:annotation
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
	at java.util.ArrayList$Itr.next(ArrayList.java:851)
	at net.sf.saxon.Gizmo.delete(Gizmo.java:439)
	at net.sf.saxon.Gizmo.executeCommands(Gizmo.java:304)
	at net.sf.saxon.Gizmo.<init>(Gizmo.java:231)
	at net.sf.saxon.Gizmo.main(Gizmo.java:168)

History

#1 Updated by Martin Honnen 23 days ago

https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html says "If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.". I think the Iterator Saxon creates breaks the contract to implement and solely use the remove method when deleting items it iterates over.

#2 Updated by Michael Kay 23 days ago

Yes, something like that must be going on, but I'm having trouble seeing exactly where. We call the delete() method on the Item affected, but that doesn't actually delete the Java object or remove it from the List of items we are iterating over.

I've established that if we change the code to just use the SequenceIterator returned by getSelectedItems(buffer, Token.EOF) directly, then it works, but I feel there must be a reason why the code wasn't written to do just that, and that changing it would break something else. Unfortunately debugging and testing Gizmo isn't a very well-developed art, partly because there have been very few problems with it.

#3 Updated by Martin Honnen 22 days ago

For delete //child, the GroundedValue all has a property value with an ArrayList of ElementImpl that I think is iterated over with for (Item item : all.asIterable()). It is not changed during the iteration and not shared with other objects.

Meanwhile, for namespace ex http://example.com and delete //ex:child that same ArrayList is shared as the _value property of the elementList property of the DocumentImpl that is the root of all elements and that way in deIndex the element nodes are removed from that ArrayList in line 509 list.remove(node); which then causes the iterator to fail as it doesn't expect any deletion from the ArrayList.

#4 Updated by Martin Honnen 21 days ago

ListIterator in public GroundedValue materialize() does return SequenceExtent.makeSequenceExtent(list); but the description of SequenceExtent.makeSequenceExtent says "@param input a List containing the items in the sequence. The caller guarantees that this list will not be subsequently modified.". So perhaps ListIterator needs to call makeSequence with a shallow copy return SequenceExtent.makeSequenceExtent(new ArrayList<>(list)); instead?

Please register to edit this issue

Also available in: Atom PDF