Stefan Oltmann
11/05/2021, 12:56 PMUncaught Kotlin exception: kotlin.Throwable: The process was terminated due to the unhandled exception thrown in the coroutine [StandaloneCoroutine{Cancelling}@2929dd8, MainDispatcher]: mutation attempt of frozen kotlin.collections.HashMap@3b0368
if I do this:
val newPhotoSourceSyncInfo = mutableMapOf<PhotoSource, SyncInfo>()
newPhotoSourceSyncInfo.putAll(oldState.photoSourceSyncInfo)
newPhotoSourceSyncInfo[photoSource] = syncInfo
return oldState.copy(
photoSourceSyncInfo = newPhotoSourceSyncInfo
)
But I can do this without any problem:
val newPhotoSources = mutableListOf<PhotoSource>()
newPhotoSources.addAll(oldState.photoSources)
newPhotoSources.add(
PhotoSource(
id = 1
)
)
return oldState.copy(
photoSources = newPhotoSources,
)
Why is that for a HashMap different and how can I do it to please the memory model?mkrussel
11/05/2021, 1:18 PMmutableMapOf
delegates to NSDictionary
, but HashMap
is using a pure Kotlin class.Stefan Oltmann
11/05/2021, 1:29 PMmutableListOf
?mkrussel
11/05/2021, 1:46 PMmutableMapOf
, from the first example, but would have expected it to also be a native class that ignores freezing.
In general even if sample two works, I would avoid it and strictly follow the kotlin native memory model.
Assume any object accessible by multiple threads are frozen and do not modify them.Stefan Oltmann
11/05/2021, 1:49 PMmkrussel
11/05/2021, 1:50 PMStefan Oltmann
11/05/2021, 1:51 PMStefan Oltmann
11/05/2021, 1:52 PMplus()
. I try thatStefan Oltmann
11/05/2021, 2:08 PMStefan Oltmann
11/05/2021, 2:11 PMList
all is good, but Map
does not workmkrussel
11/05/2021, 2:15 PMaddFeed
is called on the main thread.
Ideally what you would want to do in your example, is get the new data from a background thread if needed and then update the map on the main thread. Try to leave mutable state on just the main thread.
If you do need to modify maps on multiple threads, you could look at the stately library.Stefan Oltmann
11/05/2021, 2:32 PMkpgalligan
11/05/2021, 3:10 PMI think the freeze rules do not apply to classes from Objective-C. I'm guessing thatmutableMapOf does not use NSDictionarydelegates tomutableMapOf
, butNSDictionary
is using a pure Kotlin classHashMap
kpgalligan
11/05/2021, 3:12 PMmutableMapOf
does not use that, though. It would be easier to figure this out with an example to run, and more detail from the stack trace. Exactly where is it getting upset?Stefan Oltmann
11/05/2021, 3:12 PMkpgalligan
11/05/2021, 3:12 PMkpgalligan
11/05/2021, 3:13 PMkpgalligan
11/05/2021, 3:13 PMStefan Oltmann
11/05/2021, 3:15 PMDispatchers.Default
calling my method. This method launches a new Coroutine on Dispatchers.Main
to manipulate this map and that's where it fails. Changing the external Coroutine to Main
let's it work.
And yes, that may not be a List vs Map difference because I double-checked that indeed all List handling methods actually get called from Main coroutines. So that was my wrong assumption.Stefan Oltmann
11/05/2021, 3:17 PMprivate fun onSyncStatusUpdate(
oldState: PhotoState,
photoSource: PhotoSource,
syncInfo: SyncInfo
): PhotoState {
print("onSyncStatusUpdate() method entry: ")
logCurrentThread()
launch(Dispatchers.Main) {
print("onSyncStatusUpdate() launch block: ")
logCurrentThread()
val newPhotoSourceSyncInfo =
oldState.photoSourceSyncInfo + (photoSource to syncInfo)
// FIXME Use store.dispatch()
state.value = oldState.copy(
photoSourceSyncInfo = newPhotoSourceSyncInfo
)
}
return oldState
}
Stefan Oltmann
11/05/2021, 3:17 PMStefan Oltmann
11/05/2021, 3:18 PMkpgalligan
11/05/2021, 3:19 PMStefan Oltmann
11/05/2021, 3:23 PMkpgalligan
11/05/2021, 3:23 PMoldState.photoSourceSyncInfo + (photoSource to syncInfo)
. If the code is failing on that line, I'd assume the map is trying to optimize something under the hood with a side effect that is incompatible with the freeze model. I'd also confirm it's exactly that and not somewhere else.kpgalligan
11/05/2021, 3:24 PMStefan Oltmann
11/08/2021, 12:23 PMMap
get frozen if I put a key
into it that is frozen?Stefan Oltmann
11/08/2021, 1:18 PMkpgalligan
11/08/2021, 6:02 PMfreeze()
, that will recursively freeze everything that the object is touching. If a key is already frozen, adding that to a map won't freeze the map.Stefan Oltmann
11/09/2021, 8:10 AMkpgalligan
11/09/2021, 6:35 PMkpgalligan
11/09/2021, 6:42 PMensureNeverFrozen()
on anything that shouldn't be frozen (and maybe leave it there). We do it proactively on certain types of objects (view models, etc)Stefan Oltmann
11/10/2021, 7:24 AMWe do it proactively on certain types of objects (view models, etc)Yes, very good idea. After fixing multiple problems with accidently frozen state I now spam
ensureNeverFrozen()
all over the place for fail fast.