[K/N] Catch concurrent modifications of HashMap

As a part of efforts to stabilize Native stdlib.
This commit is contained in:
Abduqodiri Qurbonzoda
2023-05-25 03:59:49 +03:00
committed by Space Team
parent 9d8d9d000f
commit af1630e270
3 changed files with 197 additions and 6 deletions
@@ -10,15 +10,33 @@ import kotlin.native.FreezingIsDeprecated
@OptIn(FreezingIsDeprecated::class)
actual class HashMap<K, V> private constructor(
private var keysArray: Array<K>,
private var valuesArray: Array<V>?, // allocated only when actually used, always null in pure HashSet
private var presenceArray: IntArray,
private var hashArray: IntArray,
private var maxProbeDistance: Int,
private var length: Int
// keys in insert order
private var keysArray: Array<K>,
// values in insert order, allocated only when actually used, always null in pure HashSet
private var valuesArray: Array<V>?,
// hash of a key by its index, -1 if a key at that index was removed
private var presenceArray: IntArray,
// (index + 1) of a key by its hash, 0 if there is no key with that hash, -1 if collision chain continues to the hash-1
private var hashArray: IntArray,
// max length of a collision chain
private var maxProbeDistance: Int,
// index of the next key to be inserted
private var length: Int
) : MutableMap<K, V> {
private var hashShift: Int = computeShift(hashSize)
/**
* The number of times this map is structurally modified.
*
* A modification is considered to be structural if it changes the map size,
* or otherwise changes it in a way that iterations in progress may return incorrect results.
*
* This value can be used by iterators of the [keys], [values] and [entries] views
* to provide fail-fast behavoir when a concurrent modification is detected during iteration.
* [ConcurrentModificationException] will be thrown in this case.
*/
private var modCount: Int = 0
private var _size: Int = 0
override actual val size: Int
get() = _size
@@ -140,6 +158,7 @@ actual class HashMap<K, V> private constructor(
valuesArray?.resetRange(0, length)
_size = 0
length = 0
registerModification()
}
override actual val keys: MutableSet<K> get() {
@@ -206,6 +225,10 @@ actual class HashMap<K, V> private constructor(
private val capacity: Int get() = keysArray.size
private val hashSize: Int get() = hashArray.size
private fun registerModification() {
modCount += 1
}
internal fun checkIsMutable() {
if (isReadOnly) throw UnsupportedOperationException()
}
@@ -268,6 +291,7 @@ actual class HashMap<K, V> private constructor(
}
private fun rehash(newHashSize: Int) {
registerModification()
if (length > _size) compact()
if (newHashSize != hashSize) {
hashArray = IntArray(newHashSize)
@@ -339,6 +363,7 @@ actual class HashMap<K, V> private constructor(
presenceArray[putIndex] = hash
hashArray[hash] = putIndex + 1
_size++
registerModification()
if (probeDistance > maxProbeDistance) maxProbeDistance = probeDistance
return putIndex
}
@@ -367,6 +392,7 @@ actual class HashMap<K, V> private constructor(
removeHashAt(presenceArray[index])
presenceArray[index] = TOMBSTONE
_size--
registerModification()
}
private fun removeHashAt(removedHash: Int) {
@@ -534,6 +560,7 @@ actual class HashMap<K, V> private constructor(
) {
internal var index = 0
internal var lastIndex: Int = -1
private var expectedModCount: Int = map.modCount
init {
initNext()
@@ -547,14 +574,23 @@ actual class HashMap<K, V> private constructor(
fun hasNext(): Boolean = index < map.length
fun remove() {
checkForComodification()
check(lastIndex != -1) { "Call next() before removing element from the iterator." }
map.checkIsMutable()
map.removeKeyAt(lastIndex)
lastIndex = -1
expectedModCount = map.modCount
}
internal fun checkForComodification() {
if (map.modCount != expectedModCount)
throw ConcurrentModificationException()
}
}
internal class KeysItr<K, V>(map: HashMap<K, V>) : Itr<K, V>(map), MutableIterator<K> {
override fun next(): K {
checkForComodification()
if (index >= map.length) throw NoSuchElementException()
lastIndex = index++
val result = map.keysArray[lastIndex]
@@ -566,6 +602,7 @@ actual class HashMap<K, V> private constructor(
internal class ValuesItr<K, V>(map: HashMap<K, V>) : Itr<K, V>(map), MutableIterator<V> {
override fun next(): V {
checkForComodification()
if (index >= map.length) throw NoSuchElementException()
lastIndex = index++
val result = map.valuesArray!![lastIndex]
@@ -577,6 +614,7 @@ actual class HashMap<K, V> private constructor(
internal class EntriesItr<K, V>(map: HashMap<K, V>) : Itr<K, V>(map),
MutableIterator<MutableMap.MutableEntry<K, V>> {
override fun next(): EntryRef<K, V> {
checkForComodification()
if (index >= map.length) throw NoSuchElementException()
lastIndex = index++
val result = EntryRef(map, lastIndex)