-
Notifications
You must be signed in to change notification settings - Fork 62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix map iterators #73
Conversation
Because it changes iteration order of elements and leads to inconsistency if an iteration is already in progress
override fun setValue(newValue: V): V { | ||
val result = value | ||
value = newValue | ||
builder[key] = newValue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is the bug #68.
The value is updated bypassing the iterator this mutable entry was obtained from.
@@ -144,6 +147,7 @@ internal abstract class PersistentHashMapBaseIterator<K, V, T>(node: TrieNode<K, | |||
if (i > 0) { | |||
path[i - 1].moveToNextNode() | |||
} | |||
path[i].reset(TrieNode.EMPTY.buffer, 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we release iterated nodes to avoid memory leaks
// assert(buffer[nodeIndex] !== newNode) | ||
|
||
val newNodeBuffer = newNode.buffer | ||
if (newNodeBuffer.size == 2 && newNode.nodeMap == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here canonicalisation of trie can change order of iteration (see below), therefore I removed canonicalisation for mutable maps as the update may be made by an iterator currently iterating the map builder.
------------------------------ iterator.remove() after -------------------------------
| k1 | v1 | k3 | v3 | child1 | iterator.next() returned (k4, v4) | k1 | v1 | k2 | v2 | k3 | v3 |
------------------------|----- -------------------------------
| -->
---------------------
| k4 | v4 | k2 | v2 |
---------------------
mutator.size-- | ||
mutator.operationResult = valueAtKeyIndex(keyIndex) | ||
if (buffer.size == ENTRY_SIZE) return null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mutable map is not canonicalised, nodes containing a single entry can occur.
// assert(hasEntryAt(positionMask)) | ||
// assert(buffer.size > ENTRY_SIZE) // can be false only for the root node | ||
if (buffer.size == ENTRY_SIZE) return null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The persistent map could be built by a non-canonical builder
Also addresses #68