ribesg
09/12/2019, 9:59 AMkotlin.native.concurrent.InvalidMutabilityException
that I can’t explain.
companion object {
internal var xs: Set<X> by atomic(HashSet())
private set
fun registerX(x: X) {
xs = xs + x
}
}
atomic
being:
inline fun <reified T> atomic(initial: T) = AtomicDelegate(initial)
AtomicDelegate
being:
class AtomicDelegate<T>(initial: T) : ReadWriteProperty<Any?, T> {
private val atomic = AtomicReference(initial)
override fun getValue(thisRef: Any?, property: KProperty<*>): T =
atomic.get()
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
atomic.set(value)
}
}
And AtomicReference
here is:
internal actual class AtomicReference<T> actual constructor(initial: T) {
private val atomic =
kotlin.native.concurrent.FreezableAtomicReference(initial)
actual fun get(): T =
atomic.value
actual fun set(newValue: T) {
atomic.compareAndSet(get(), newValue)
}
}
The app crashes when registerX
is called:
Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlin.collections.HashSet@221c288
Where in my code am I attempting to modify a frozen HashSet
? I’m pretty sure I’m just creating a new local one, modifying it, then assigning it which makes it frozen (that’s what the +
does)Marko Mitic
09/12/2019, 10:14 AMMarko Mitic
09/12/2019, 10:17 AMsetValue
should look like this: atomic.set(value.freeze())
ribesg
09/12/2019, 10:22 AMribesg
09/12/2019, 10:23 AMMarko Mitic
09/12/2019, 10:24 AMMarko Mitic
09/12/2019, 10:25 AMribesg
09/12/2019, 10:28 AMAtomicReference
is actually a custom thing, it’s a multiplatform expect, I’m adding it to my main messageribesg
09/12/2019, 10:29 AMatomic.compareAndSet(get(), newValue.freeze())
instead of atomic.compareAndSet(get(), newValue)
Marko Mitic
09/12/2019, 10:30 AMMarko Mitic
09/12/2019, 10:35 AMMarko Mitic
09/12/2019, 10:36 AMDominaezzz
09/12/2019, 10:47 AMxs + x
. Is that the entire stacktrace?ribesg
09/12/2019, 11:02 AMribesg
09/12/2019, 11:03 AMobject
`var`s who need to use my atomic
delegate, but other than that it’s kinda straightforward. We already have a production Android/iOS app with common backendsribesg
09/12/2019, 11:06 AM+
(from kotlin):
public operator fun <T> Set<T>.plus(element: T): Set<T> {
val result = LinkedHashSet<T>(mapCapacity(size + 1))
result.addAll(this)
result.add(element)
return result
}
add
and addAll
are the only mutations I see, and they should work because result
should not be frozen, unless I don’t understand somethingribesg
09/12/2019, 11:12 AMDominaezzz
09/12/2019, 11:13 AMHashSet
is being mutated.Marko Mitic
09/12/2019, 11:24 AMribesg
09/12/2019, 12:10 PMxs = xs + x
with xs = setOf(x, *xs.toTypedArray())
and it seems to work. So the local hash map created by +
is frozen?louiscad
09/12/2019, 12:26 PMFrozenDelegate
works well for me: https://github.com/LouisCAD/Splitties/blob/fb8a6266fe84443dca91d9ef73abcdabe0ff0275/modules/preferences/src/appleMain/kotlin/splitties/preferences/FrozenDelegate.ktribesg
09/12/2019, 1:41 PMribesg
09/12/2019, 1:42 PMunregisterX
function, I don’t see how I can do that without modifying collections